¿Hay alguna manera de forzar que todos los ensamblados a los que se hace referencia se carguen en el dominio de la aplicación?

Mis proyectos están configurados así:

  • Definición del proyecto”
  • Implementacion de proyecto”
  • Proyecto “Consumidor”

El proyecto “Consumidor” hace referencia tanto a “Definición” como a “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 encontrar tipos en “Implementación”.

¿Hay alguna manera de forzar que cualquier ensamblado al que se haga referencia se cargue en App Domain sin conocer la ruta o el nombre, y preferiblemente sin tener que usar un marco IOC completo?

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ñaló Jon, la solución ideal debería recurrir a las dependencias para cada uno de los ensamblajes cargados, pero en mi caso específico no tengo que preocuparme por ello.


Actualización: El Marco de Extensibilidad Administrado (System.ComponentModel) incluido en .NET 4 tiene instalaciones mucho mejores para lograr cosas como esta.

Puede usar Assembly.GetReferencedAssemblies para obtener un AssemblyName[] , y luego llamar a Assembly.Load(AssemblyName) en cada uno de ellos. Necesitarás recurse, por supuesto, pero preferiblemente haciendo un seguimiento de los ensamblajes que ya has cargado 🙂

solo quería compartir un ejemplo recursivo. Estoy llamando 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)); } } } 

Si usa Fody.Costura o cualquier otra solución de fusión de ensamblaje, la respuesta aceptada no funcionará.

Lo siguiente carga los ensamblados a los que se hace referencia de cualquier ensamblado cargado actualmente. 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))); 

Al ver que tenía que cargar un ensamblado + dependencias de una ruta específica hoy, escribí esta clase para hacerlo.

 public static class AssemblyLoader { private static readonly ConcurrentDictionary AssemblyDirectories = new ConcurrentDictionary(); 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 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); } } 

Otra versión (basada en la respuesta de Daniel Schaffer ) es el caso en el que no es necesario cargar todos los ensamblajes, sino un número predefinido de ellos:

 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 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!"); }