Meine Projekte sind wie folgt aufgebaut:
- Projektdefinition"
- Projekt "Implementierung"
- Projekt "Verbraucher"
Das Projekt "Consumer" verweist sowohl auf "Definition" als auch auf "Implementation", verweist jedoch statisch nicht auf Typen in "Implementation".
Wenn die Anwendung gestartet wird, ruft das Projekt "Consumer" in "Definition" eine statische Methode auf, die in "Implementierung" Typen finden muss.
Gibt es eine Möglichkeit, das Laden einer Assembly, auf die verwiesen wird, in die App-Domäne zu erzwingen, ohne den Pfad oder Namen zu kennen und vorzugsweise ohne ein vollwertiges IOC-Framework verwenden zu müssen?
c#
assemblies
appdomain
Daniel Schaffer
quelle
quelle
Antworten:
Dies schien den Trick zu tun:
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList(); var loadedPaths = loadedAssemblies.Select(a => a.Location).ToArray(); var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll"); var toLoad = referencedPaths.Where(r => !loadedPaths.Contains(r, StringComparer.InvariantCultureIgnoreCase)).ToList(); toLoad.ForEach(path => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path))));
Wie Jon bemerkte, müsste die ideale Lösung in die Abhängigkeiten für jede der geladenen Assemblys zurückgreifen, aber in meinem speziellen Szenario muss ich mir darüber keine Sorgen machen.
Update: Das in .NET 4 enthaltene Managed Extensibility Framework (System.ComponentModel) bietet viel bessere Möglichkeiten, um solche Dinge zu erreichen.
quelle
new DirectoryCatalog(".");
(Referenzierung erforderlichSystem.ComponentModel.Composition
).assembly.Location
. Das muss man auch überprüfen. Funktioniert nicht fürIsDynamic
Baugruppen.Sie können verwenden
Assembly.GetReferencedAssemblies
, um eine zu erhaltenAssemblyName[]
, und dannAssembly.Load(AssemblyName)
jeden von ihnen aufrufen . Sie müssen natürlich wiederkehren - aber am besten behalten Sie die Baugruppen im Auge, die Sie bereits geladen haben :)quelle
System.Reflection
) verwendet wird, werden die in Baugruppen enthaltenen Dienste, die noch nicht geladen wurden, natürlich nicht gefunden. Seitdem habe ich standardmäßig eine Dummy-Unterklasse aus einem zufälligen Typ jeder referenzierten Assembly im CompositionRoot meiner App erstellt, um sicherzustellen, dass alle Abhängigkeiten vorhanden sind. Ich hoffe, ich kann diesen Unsinn überspringen, indem ich alles im Voraus lade, auch auf Kosten einer weiteren Verlängerung der Startzeit. @ JonSkeet gibt es eine andere Möglichkeit, dies zu tun? thxwollte nur ein rekursives Beispiel teilen. Ich rufe die LoadReferencedAssembly-Methode in meiner Startroutine folgendermaßen auf:
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { this.LoadReferencedAssembly(assembly); }
Dies ist die rekursive Methode:
private void LoadReferencedAssembly(Assembly assembly) { foreach (AssemblyName name in assembly.GetReferencedAssemblies()) { if (!AppDomain.CurrentDomain.GetAssemblies().Any(a => a.FullName == name.FullName)) { this.LoadReferencedAssembly(Assembly.Load(name)); } } }
quelle
name
nicht bereitsAppDomain.CurrentDomain.GetAssemblies()
, was bedeutet , dass es nur wiederholen, wenn dasforeach
gepflücktAssemblyName
noch nicht geladen ist .O(n^2)
Laufzeit dieses Algorithmus (GetAssemblies().Any(...)
innerhalb von aforeach
)). Ich würde ein verwendenHashSet
, um dies auf etwas in der Größenordnung von zu bringenO(n)
.Wenn Sie Fody.Costura oder eine andere Lösung zum Zusammenführen von Baugruppen verwenden, funktioniert die akzeptierte Antwort nicht.
Im Folgenden werden die referenzierten Baugruppen einer aktuell geladenen Baugruppe geladen. Die Rekursion bleibt Ihnen überlassen.
var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList(); loadedAssemblies .SelectMany(x => x.GetReferencedAssemblies()) .Distinct() .Where(y => loadedAssemblies.Any((a) => a.FullName == y.FullName) == false) .ToList() .ForEach(x => loadedAssemblies.Add(AppDomain.CurrentDomain.Load(x)));
quelle
!y.IsDynamic
in Ihrem.Where
Da ich heute eine Assembly + Abhängigkeiten von einem bestimmten Pfad laden musste, habe ich diese Klasse geschrieben, um dies zu tun.
public static class AssemblyLoader { private static readonly ConcurrentDictionary<string, bool> AssemblyDirectories = new ConcurrentDictionary<string, bool>(); static AssemblyLoader() { AssemblyDirectories[GetExecutingAssemblyDirectory()] = true; AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly; } public static Assembly LoadWithDependencies(string assemblyPath) { AssemblyDirectories[Path.GetDirectoryName(assemblyPath)] = true; return Assembly.LoadFile(assemblyPath); } private static Assembly ResolveAssembly(object sender, ResolveEventArgs args) { string dependentAssemblyName = args.Name.Split(',')[0] + ".dll"; List<string> directoriesToScan = AssemblyDirectories.Keys.ToList(); foreach (string directoryToScan in directoriesToScan) { string dependentAssemblyPath = Path.Combine(directoryToScan, dependentAssemblyName); if (File.Exists(dependentAssemblyPath)) return LoadWithDependencies(dependentAssemblyPath); } return null; } private static string GetExecutingAssemblyDirectory() { string codeBase = Assembly.GetExecutingAssembly().CodeBase; var uri = new UriBuilder(codeBase); string path = Uri.UnescapeDataString(uri.Path); return Path.GetDirectoryName(path); } }
quelle
Eine weitere Version (basierend auf der Antwort von Daniel Schaffer ) ist der Fall, wenn Sie möglicherweise nicht alle Baugruppen laden müssen, sondern eine vordefinierte Anzahl davon:
var assembliesToLoad = { "MY_SLN.PROJECT_1", "MY_SLN.PROJECT_2" }; // First trying to get all in above list, however this might not // load all of them, because CLR will exclude the ones // which are not used in the code List<Assembly> dataAssembliesNames = AppDomain.CurrentDomain.GetAssemblies() .Where(assembly => AssembliesToLoad.Any(a => assembly.GetName().Name == a)) .ToList(); var loadedPaths = dataAssembliesNames.Select(a => a.Location).ToArray(); var compareConfig = StringComparison.InvariantCultureIgnoreCase; var referencedPaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll") .Where(f => { // filtering the ones which are in above list var lastIndexOf = f.LastIndexOf("\\", compareConfig); var dllIndex = f.LastIndexOf(".dll", compareConfig); if (-1 == lastIndexOf || -1 == dllIndex) { return false; } return AssembliesToLoad.Any(aName => aName == f.Substring(lastIndexOf + 1, dllIndex - lastIndexOf - 1)); }); var toLoad = referencedPaths.Where(r => !loadedPaths.Contains(r, StringComparer.InvariantCultureIgnoreCase)).ToList(); toLoad.ForEach(path => dataAssembliesNames.Add(AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(path)))); if (dataAssembliesNames.Count() != AssembliesToLoad.Length) { throw new Exception("Not all assemblies were loaded into the project!"); }
quelle
Wenn Sie Assemblys haben, auf die zur Kompilierungszeit kein Code referenziert wird, werden diese Assemblys nicht als Referenz auf Ihre andere Assembly aufgenommen, selbst wenn Sie das Projekt- oder Nuget-Paket als Referenz hinzugefügt haben. Dies ist unabhängig von
Debug
oderRelease
Build-Einstellungen, Code-Optimierung usw. In diesen Fällen müssen Sie explizit aufrufenAssembly.LoadFrom(dllFileName)
, um die Assembly zu laden.quelle
Um die referenzierte Assembly nach Namen zu erhalten, können Sie die folgende Methode verwenden:
public static Assembly GetAssemblyByName(string name) { var asm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == name); if (asm == null) asm = AppDomain.CurrentDomain.Load(name); return asm; }
quelle
In meiner Winforms-Anwendung gebe ich JavaScript (in einem WebView2-Steuerelement) die Möglichkeit, verschiedene .NET-Dinge aufzurufen, beispielsweise Methoden
Microsoft.VisualBasic.Interaction
in der Assembly Microsoft.VisualBasic.dll (zInputBox()
usw.).Meine Anwendung als solche verwendet diese Assembly jedoch nicht, sodass die Assembly nie geladen wird.
Um das Laden der Assembly zu erzwingen, habe ich dies einfach in Form1_Load hinzugefügt:
if (DateTime.Now < new DateTime(1000, 1, 1, 0, 0, 0)) { // never happens Microsoft.VisualBasic.Interaction.Beep(); // you can add more things here }
Der Compiler glaubt, dass die Assembly möglicherweise benötigt wird, aber in Wirklichkeit passiert dies natürlich nie.
Keine sehr ausgefeilte Lösung, aber schnell und schmutzig.
quelle