2015-11-28 2 views
1

Вот как мои папки приложения выглядит следующим образом:Правильный способ разрешения сборок из подпапок

Application: 
+ App.exe 
+ App.exe.config 
Application/Plugins: 
+ Plugin1 (folder) 
Application/Plugins/Plugin1: 
+ Plugin1.dll 
+ SomeDll.dll 

Так главное приложение App.exe ищет папку плагинов и загрузки {PluginName} .dll в памяти, чтобы запустить его. Этот плагин обычно использует собственные зависимые сборки, которые должны быть загружены (например, SomeDll.dll). Похоже, что иногда возникают серьезные проблемы. Я получаю исключение, потому что, например, зависимая сборка зависимой сборки не может быть найдена, и я не знаю, почему.

Например, Мой плагин должен загружать много дополнительных библиотек dll, потому что плагин запускает службу OwinSelfHost.

Так он должен загрузить, например:

System.Web.Http.Owin 
Owin 
Microsoft.Owin 
Microsoft.Owin.Host.HttpListener 
Microsoft.Owin.Hosting 

и когда нагрузка Microsoft.Owin.Hosting затем бросить исключение, которое не может загрузить Microsoft.Owin

Исключение выглядит следующим образом:

Could not load file or assembly 'Microsoft.Owin, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of it's dependencies. File not found. 
+0

Почему вы не ставите все DLL-файлы в одну папку при создании приложения? –

+0

Поймать его будет много беспорядка. Я стараюсь держать его в чистоте – Puchacz

+1

@IlyaKogan, имеющий всю DLL в местоположении, найденном по умолчанию (aka [пробный путь] (https://msdn.microsoft.com/en-us/library/823z9h8w.aspx?f=255&MSPPError=-2147217396)) значительно менее интересен, чем ударить все конфликты нагрузки во время выполнения. Я полностью понимаю, почему OP хочет держать его интересным и очищать беспорядок, когда они шеи глубоко в нем :) –

ответ

1

Я написал этот метод для разрешения сборок. Он подстраивается под мои потребности.

Он в основном подключает событие AssemblyResolve к текущему домену приложения для извлечения запрошенной сборки из списка каталогов.

Существует нет простого способа найти, где файл сборки, соответствующий пространству имен, разрешается, за исключением загрузки файла сборки и проверки того, к какому пространству имён принадлежит.

Плюс, он отбрасывает некоторые нежелательные сборки (например, сериализаторы, ресурсы ...) и обнаруживает DLL или exes, которые не являются сборками .NET.

Лучший подход состоит в использовании Global Assembly Cache, но мы хотим, чтобы наши плагины были полностью подвижными. Так вот оно.

public static class AssemblyResolver 
{ 
    internal static void Hook(params string[] folders) 
    { 
     AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => 
      { 
       // Check if the requested assembly is part of the loaded assemblies 
       var loadedAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name); 
       if (loadedAssembly != null) 
        return loadedAssembly; 

       // This resolver is called when an loaded control tries to load a generated XmlSerializer - We need to discard it. 
       // http://connect.microsoft.com/VisualStudio/feedback/details/88566/bindingfailure-an-assembly-failed-to-load-while-using-xmlserialization 

       var n = new AssemblyName(args.Name); 

       if (n.Name.EndsWith(".xmlserializers", StringComparison.OrdinalIgnoreCase)) 
        return null; 

       // http://stackoverflow.com/questions/4368201/appdomain-currentdomain-assemblyresolve-asking-for-a-appname-resources-assembl 

       if (n.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) 
        return null; 

       string assy = null; 

       // Find the corresponding assembly file 
       foreach (var dir in folders) 
       { 
        assy = new[] { "*.dll", "*.exe" }.SelectMany(g => Directory.EnumerateFiles(dir, g)).FirstOrDefault(f => 
           { 
            try { return n.Name.Equals(AssemblyName.GetAssemblyName(f).Name, StringComparison.OrdinalIgnoreCase); } 
            catch (BadImageFormatException) { return false; /* Bypass assembly is not a .net exe */ } 
            catch (Exception ex) { throw new ApplicationException("Error loading assembly " + f, ex); } 
           }); 

        if (assy != null) 
         return Assembly.LoadFrom(assy); 
       } 

       throw new ApplicationException("Assembly " + args.Name + " not found"); 
      }; 
    } 
} 

Вот как это работает:

AssemblyResolver.Hook("\Plugins", "\CommonReferences"); 

Everytime нуждается некоторые узлы, которые будут решены, он получит тот, который загружается в память, в противном случае он будет искать в любых заданных папках.

Смежные вопросы