2010-03-05 9 views
60

Mis proyectos se establecen así:¿Hay alguna manera de forzar que todos los ensamblados a los que se hace referencia se carguen en el dominio de la aplicación?

  • Proyecto "Definición"
  • Proyecto "Aplicación"
  • Proyecto "consumidor"

proyecto hace referencia a "consumidor" ambos "Definición" y "Implementación", pero no hace referencia estática a ningún tipo en "Implementación".

Cuando se inicia la aplicación, el Proyecto "consumidor" llama a un método estático en "Definición", que necesita para encontrar tipos de "Aplicación"

¿Hay alguna manera de forzar cualquier ensamblaje de referencia para ser cargado en el ¿App Domain sin conocer la ruta o el nombre, y preferiblemente sin tener que usar un marco IOC completo?

+1

¿Qué tipo de problema está causando? ¿Por qué necesitas forzar la carga? –

+0

No se está cargando en absoluto, presumiblemente porque no hay una dependencia estática –

+0

¿Cómo está tratando de "encontrar tipos" en la implementación? ¿Estás buscando algo que implemente una interfaz específica? –

Respuesta

68

Esto parecía hacer el truco:

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

Como se señaló Jon, la solución ideal sería necesario recurrir en las dependencias para cada uno de los ensamblados cargados, pero en mi escenario específico no tengo que preocuparme por ello.


Actualización: El Marco Extensibilidad Administrada (System.ComponentModel) incluido en .NET 4 tiene mucho mejores instalaciones para llevar a cabo este tipo de cosas.

+3

Esto no funciona para mí, mis ensamblados a los que hace referencia, que no están cargados, no aparecen en AppDomain.CurrentDomain.GetAssemblies() .. Hmm ... – Ted

+8

¿Qué características? No he encontrado nada mediante la búsqueda. – Nuzzolilo

+3

Usando MEF, el código anterior puede acortarse a: 'nuevo DirectoryCatalog (". ");' (Requiere referencia 'System.ComponentModel.Composition'). –

47

Puede usar Assembly.GetReferencedAssemblies para obtener un AssemblyName[], y luego llamar al Assembly.Load(AssemblyName) en cada uno de ellos. Tendrá que recursivo, por supuesto - pero preferiblemente hacer el seguimiento de las asambleas que ya ha cargado :)

+0

Lo encontré, pero el problema es que tengo que hacer lo que sea que esté haciendo desde el ensamblado al que se hace referencia ...y al menos en el contexto de una prueba unitaria, GetCallingAssembly, GetExecutingAssembly devuelve, por supuesto, el ensamblado al que se hace referencia, y GetEntryAssembly devuelve null: \ –

+4

Si está después de cargar ensamblados de referencia, lo anterior resolverá su problema. También puede preguntar un tipo de tipo específico de (T). Montaje si eso ayuda. Tengo la sensación de que lo que necesita es cargar dinámicamente los ensamblados que contienen la implementación (no referenciados). Si este es el caso, deberá mantener una lista estática del nombre y cargarla manualmente o recorrer todo su directorio, cargar y luego buscar el tipo con las interfaces correctas. –

+0

Parece extraño que este tipo de funcionalidad no esté incorporada en el BCL: esta publicación es bastante antigua, ¿alguien sabe que algo así se agregó al framework desde entonces? – mfeineis

10

Si utiliza Fody.Costura, o cualquier otra solución de montaje fusión, la respuesta aceptada no va a funcionar.

Las siguientes cargas de los ensamblados de referencia de cualquier Asamblea cargado en ese momento. La recursividad te queda a ti.

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

¿Deseas asesorar dónde debería ir este fragmento? – Telemat

+1

en su cargador de arranque/puesta en marcha, me imagino. –

+0

Es posible que esté equivocado, pero creo que puede comprobar '! Y.IsDynamic' en su' .Where' – Felype

12

solo queríamos compartir un ejemplo recursivo. Voy a llamar al método LoadReferencedAssembly en mi rutina de inicio de esta manera:

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

Este es el método recursivo:

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

Me pregunto si las referencias de ensamblaje circular podrían provocar excepciones de desbordamiento de pila. –

+0

Ronnie, creo que no, el código solo ejecuta la recursión en caso de que 'name' ya no esté en' AppDomain.CurrentDomain.GetAssemblies() ', lo que significa que solo se repetirá si' foreach' elegido 'AssemblyName' aún no está activo cargado. – Felype

0

En vista de que he tenido que cargar un ensamblado + dependencias de una ruta específica hoy escribió esta clase para hacerlo.

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); 
    } 
} 
Cuestiones relacionadas