c# - aplicación - dominio de aplicacion definicion
¿Hay alguna manera de forzar que todos los ensamblados a los que se hace referencia se carguen en el dominio de la aplicación? (6)
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<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);
}
}
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.
Otra versión (basada en la respuesta de ) 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<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!");
}
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 :)
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)));
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));
}
}
}