Есть ли способ заставить все ссылочные сборки загружаться в домен приложения?
Мои проекты настроены следующим образом:
- Проект "Определение"
- Проект "Внедрение"
- Проект "Потребитель"
Проект "Потребитель" ссылается как на "Определение", так и "Реализация" , но не статически ссылается на любые типы в "Реализация" .
Когда приложение запускается, Project "Consumer" вызывает статический метод в "Определении", который должен найти типы в "Реализация"
Есть ли способ заставить любую ссылочную сборку загружаться в домен приложения без знания пути или имени и, желательно, без использования полноценной структуры IOC?
Ответы
Ответ 1
Казалось, это трюк:
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))));
Как отметил Джон, идеальное решение необходимо будет учитывать в зависимостях для каждой из загруженных сборок, но в моем конкретном сценарии мне не нужно беспокоиться об этом.
Обновление: Рамка Managed Extensibility Framework (System.ComponentModel), включенная в .NET 4, имеет гораздо лучшие возможности для выполнения таких задач.
Ответ 2
Вы можете использовать Assembly.GetReferencedAssemblies
, чтобы получить AssemblyName[]
, а затем вызвать Assembly.Load(AssemblyName)
для каждого из них. Разумеется, вам нужно будет записаться, но желательно следить за сборками, которые вы уже загрузили:)
Ответ 3
просто хотел поделиться рекурсивным примером. Я вызываю метод LoadReferencedAssembly в моей программе запуска следующим образом:
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
this.LoadReferencedAssembly(assembly);
}
Это рекурсивный метод:
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));
}
}
}
Ответ 4
Если вы используете Fody.Costura или любое другое решение для слияния сборки, принятый ответ не будет работать.
Следующая загрузка ссылочных сборок любой загруженной в данный момент сборки. Рекурсия оставлена вам.
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)));
Ответ 5
Видя, как мне приходилось загружать сборки + зависимости из определенного пути, я написал этот класс для этого.
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);
}
}