2013-10-26 2 views
1

Я делаю паутину. сервис, который загружает файл сборки по входному параметру. Затем в сборке попытается найти определенный тип (унаследованный от определенного интерфейса), создать экземпляр и вернуть результат метода.Загрузка и разгрузка сборки в собственном AppDomain

Мне нужно, чтобы звонок к сборке был снова выпущен.

Из входных параметров метода я нахожу путь к сборке в web.config. и попробуйте загрузить его.

Это код, который работает:

[WebMethod] 
    public String[] GetData(String confKey) 
    { 
     var assemblyPath = ConfigurationManager.AppSettings[confKey]; 
     var assembly = Assembly.LoadFrom(assemblyPath); 

     List<String> retVals = new List<String>(); 

     foreach (var t in assembly.GetTypes()) 
     { 
      if (t.ImplementsInterface(typeof(IMyServiceProvider))) 
      { 
       IMyServiceProvider objectInstance = Activator.CreateInstance(t) as IMyServiceProvider; 
       retVals.Add(objectInstance.GetData()); 
      } 
     } 

     return retVals.ToArray(); 
    } 

Но этот способ можно удалить загруженную сборку или заменить его, так как файл «заблокирован».

Так что я попытался пойти другим путем и загрузить сборку в собственный AppDomain, как это:

[WebMethod] 
    public String[] GetData(String confKey) 
    { 
     var assemblyPath = ConfigurationManager.AppSettings[confKey]; 

     var tmp = String.Concat("AppDomain", Guid.NewGuid().ToString("N")); 
     AppDomain dom = AppDomain.CreateDomain(tmp, null, AppDomain.CurrentDomain.SetupInformation); 
     AssemblyName assemblyName = new AssemblyName(); 
     assemblyName.CodeBase = assemblyPath; 
     Assembly assembly = dom.Load(assemblyPath); 

     List<String> retVals = new List<String>(); 

     foreach (var t in assembly.GetTypes()) 
     { 
      if (t.ImplementsInterface(typeof(IMyServiceProvider))) 
      { 
       IMyServiceProvider objectInstance = Activator.CreateInstance(t) as IMyServiceProvider; 
       retVals.Add(objectInstance.GetData()); 
      } 
     } 

     AppDomain.Unload(dom); 
     return retVals.ToArray(); 
    } 

Но это решение выбрасывается исключение:

Не удалось загрузить файл или сборку «NameOfMyAssembly 'или одной из его зависимостей. Данное имя сборки или кодовая база недействительны. (Исключение из HRESULT: 0x80131047) - на System.Reflection.AssemblyName.nInit (RuntimeAssembly & сборки, булевой forIntrospection, Boolean raiseResolveEvent) в System.Reflection.RuntimeAssembly.CreateAssemblyName (String assemblyString, Boolean forIntrospection, RuntimeAssembly & assemblyFromResolveEvent) в системе .Reflection.RuntimeAssembly.InternalLoad (String assemblyString, Evidence assemblySecurity, StackCrawlMark & stackMark, IntPtr pPrivHostBinder, Boolean forIntrospection) на System.Reflection.RuntimeAssembly.InternalLoad (String assemblyString, Evidence assemblySecurity, StackCrawlMark & stackMark, Boolean forIntrospection) в системе. AppDomain.Load (String assemblyString) в System.AppDomain.Loa d (Строка assemblyString)

Почему первая сборка решения загружается без проблем, а вторая вызывает ошибку?

+0

Почему не используя MEF и динамически загружать все библиотеки? – Venson

ответ

1

метод dom.Load (string) пытается загрузить сборку по ее полному имени, которое состоит из имени сборки, версии, информации о культуре и открытого ключа. CLR выполняет поиск сборки в GAC и каталоге приложения. Также вы можете установить дополнительные пути поиска с помощью .config file (app.config or) и зондирование элемент. В вашем случае, как я понимаю assemblyPath - это путь к сборке, которую вы хотите загрузить, и это не то, что нужно передать в качестве параметра. Я использовал следующий код для загрузки сборки в созданном домене:

var domain = AppDomain.CreateDomain("Plugin domain"); // Domain for plugins 
domain.Load(typeof(IPlugin).Assembly.FullName); // Load assembly containing plugin interface to domain 
// ... 

var asm = Assembly.LoadFrom(pluginFile); 
foreach (var exportedType in asm.GetExportedTypes()) 
{ 
    if (!typeof (IPlugin).IsAssignableFrom(exportedType)) 
     continue; // Check if exportedType implement IPlugin interface 
    domain.Load(asm.FullName); // If so load this dll into domain 
    // ... 
} 

Поскольку все плагины лежит в «\ Plugins» директория сравнительно каталог приложения Я также добавил .config файла:

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
    <runtime> 
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> 
     <probing privatePath="Plugins"/> 
    </assemblyBinding> 
    </runtime> 
</configuration> 

Pay attantion, что если вы чтобы использовать dom.Load (AssemblyName) с указанным только свойством CodeBase, вы должны установить его в формате Uri (то есть «file: /// D:/Dir/...«), И я думаю, что эти узлы должны быть сильно названы, если они не находятся в папке текущего приложения.

Кроме того, как я вижу, вы usig Activator.CreateInstance() для создания объектов. В этом случае, хотя вы загружаете сборка в созданной области вы создаете объекты в текущем домене и весь код этого объекты будут выполнены в текущем домене тоже. Для создания и выполнения кода в отдельном домене, вы должны использовать appDomain.CreateInstanceAndUnwrap() метод (link)

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