Есть ли способ, чтобы заставить все сборки загружаются в домен приложения?
мои проекты настроены так:
- "Определения"
"Реализация"
"Потребитель"
проект " потребитель "ссылается как на" определение", так и на" реализацию", но не статически ссылается на какие-либо типы в"реализации".
при запуске приложения проект " потребитель "вызывает статический метод в" определении", который должен найти типы в"реализации"
Is есть способ заставить любую ссылочную сборку загружаться в домен приложения, не зная пути или имени, и предпочтительно без использования полноценной инфраструктуры IOC?
6 ответов:
Это, казалось, сделать трюк:
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))));
Как отметил Джон, идеальное решение должно было бы рекурсировать в зависимости для каждой из загруженных сборок, но в моем конкретном сценарии мне не нужно беспокоиться об этом.
обновление: Управляемый Фреймворк Расширяемости (System.ComponentModel), включенный в .NET 4, имеет гораздо лучшие возможности для выполнения таких вещей.
можно использовать
Assembly.GetReferencedAssemblies
для полученияAssemblyName[]
, а затем вызватьAssembly.Load(AssemblyName)
на каждом из них. Конечно, вам нужно будет рекурсировать, но желательно отслеживать сборки, которые вы уже загрузили:)
просто хотел поделиться рекурсивный пример. Я вызываю метод 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)); } } }
Если вы используете 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)));
видя, как я должен был загрузить сборку + зависимость от определенного пути, сегодня я написал этот класс, чтобы сделать это.
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); } }
еще одна версия (на основе Даниил Шаффер ответ) - это тот случай, когда вам может не понадобиться загружать все сборки, но предварительно определенное их количество:
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!"); }