2010-03-05 12 views
60

Meine Projekte sind wie folgt aufgebaut:Gibt es eine Möglichkeit zum Erzwingen, dass alle referenzierten Assemblys in die App-Domäne geladen werden?

  • Projekt "Definition"
  • Projekt "Implementierung"
  • Projekt "Consumer"

Projekt "Consumer" Referenzen sowohl "Definition" und "Implementierung", referenziert jedoch keine Typen in "Implementierung".

Wenn die Anwendung gestartet, Projekt „Consumer“, ruft eine statische Methode in „Definition“, die Typen in „Implementierung“

Gibt es finden muss eine Weise, die ich jede referenzierte Assembly in dem zu ladende zwingen kann, App Domain ohne den Pfad oder den Namen zu kennen und vorzugsweise ohne ein vollwertiges IOC-Framework zu verwenden?

+1

Welche Art von Problem verursacht es? Warum müssen Sie das Laden erzwingen? –

+0

Es wird überhaupt nicht geladen, vermutlich weil es keine statische Abhängigkeit gibt. –

+0

Wie versuchen Sie, in der Implementierung Typen zu finden? Suchen Sie nach etwas, das eine bestimmte Schnittstelle implementiert? –

Antwort

68

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)))); 

Als Jon erwähnt, ist die ideale Lösung müssten um in die Abhängigkeiten für jede der geladenen Assemblies zu rekapitulieren, aber in meinem speziellen Szenario muss ich mich nicht darum kümmern.


Update: Die Managed Extensibility Framework (System.ComponentModel) in .NET 4 enthalten hat viel bessere Möglichkeiten für Dinge wie dies zu erreichen.

+3

Das funktioniert nicht für mich, meine referenzierten Assemblys, die nicht geladen sind, werden nicht in AppDomain.CurrentDomain.GetAssemblies() angezeigt .. Hmm ... – Ted

+8

Welche Einrichtungen? Ich habe nichts über das Suchen gefunden. – Nuzzolilo

+3

Mithilfe von MEF kann der obige Code wie folgt verkürzt werden: 'new DirectoryCatalog (". ");' (Erfordert die Referenzierung von 'System.ComponentModel.Composition'). –

47

Sie können Assembly.GetReferencedAssemblies verwenden, um eine AssemblyName[] zu erhalten, und rufen Sie dann Assembly.Load(AssemblyName) auf jedem von ihnen. Sie werden Rekursion benötigen, natürlich - aber vorzugsweise die Verfolgung von Baugruppen haben Sie bereits geladen :)

+0

Ich habe das gefunden, aber das Problem ist, dass ich tun muss, was auch immer ich von der referenzierten Assembly mache ...und zumindest im Rahmen eines Komponententests gibt GetCallingAssembly, GetExecutingAssembly natürlich die referenzierte Assembly zurück, und GetEntryAssembly gibt null zurück: \ –

+4

Wenn Sie nach dem Laden von Referenz-Assemblies sind, löst das obige Ihr Problem. Sie können auch einen bestimmten Typ typeof (T) .Assembly fragen, wenn das hilft. Ich habe das Gefühl, dass Sie die Assemblys dynamisch laden müssen, die die Implementierung enthalten (nicht referenziert). Wenn dies der Fall ist, müssen Sie entweder eine statische Liste des Namens behalten und sie manuell laden oder durch Ihr gesamtes Verzeichnis gehen, laden und dann den Typ mit den richtigen Schnittstellen finden. –

+0

Es ist merkwürdig, dass diese Art von Funktionalität nicht in die BCL integriert ist - dieser Beitrag ist ziemlich alt, jemand, der weiß, dass so etwas dem Framework seither hinzugefügt wurde? – mfeineis

10

Wenn Sie Fody.Costura oder eine andere Assembly-Merging-Lösung verwenden, funktioniert die akzeptierte Antwort nicht.

Im Folgenden werden die referenzierten Assemblys aller derzeit geladenen Assembly geladen. Rekursion bleibt dir ü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))); 
+0

Bitte beraten, wo dieser Ausschnitt hinführen sollte? – Telemat

+1

in Ihrem Bootloader/Start-up Ich stelle mir vor. –

+0

Ich kann falsch liegen, aber ich denke, Sie können einfach nach '! Y.IsDynamic' in Ihrem' .Where' überprüfen. – Felype

12

wollte nur ein rekursives Beispiel teilen. Ich rufe die LoadReferencedAssembly Methode in meiner Startroutine wie folgt aus:

foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) 
{ 
    this.LoadReferencedAssembly(assembly); 
} 

Dies ist die rekursive Methode ist:

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)); 
     } 
    } 
} 
+3

Ich frage mich, ob zirkuläre Assemblyverweise dazu führen können, dass Stapelüberlauf-Ausnahmen ausgelöst werden. –

+0

Ronnie, ich glaube nicht, der Code führt nur die Rekursion aus, wenn 'name' nicht bereits in' AppDomain.CurrentDomain.GetAssemblies() 'ist, was bedeutet, dass es nur dann wiederkehrt, wenn' foreach' 'AssemblyName' noch nicht ausgewählt ist geladen. – Felype

0

Da ich heute eine Assembly + Abhängigkeiten von einem bestimmten Pfad zu laden hatte ich schrieb diese Klasse, um es 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); 
    } 
}