2012-06-08 5 views
1

Это действительно длинный, так что повесьте трубку.Почему Powershell не видит, что он уже загрузил эту сборку?

Идея состоит в том, что приложение, ссылающееся на Core.dll (см. Ниже), создаст новый объект типа Core, и при этом конструктор сканирует папку C:\Problems для любых DLL, содержащих тип который использует интерфейс IPlugin.

Он работает, когда я ссылаюсь на Core.dll из консольного приложения. Это не работает, когда я делаю эквивалент от PowerShell, если Core.dll не находится в GAC.

Эти детали:

У меня есть сборка (Core.dll), который является результатом:

public class Core 
{ 
    List<IPlugin> plugins = new List<IPlugin>(); 

    public Core(string path) 
    { 
     LoadPlugins(path); 
     foreach (IPlugin plugin in plugins) 
      plugin.Work(); 
    } 

    void LoadPlugins(string path) 
    { 
     foreach (string file in Directory.GetFiles(path)) 
     { 
      FileInfo fileInfo = new FileInfo(file); 
      Assembly dllAssembly = Assembly.LoadFile(file); 
      foreach (Type type in dllAssembly.GetTypes()) 
      { 
       Type typeInterface = type.GetInterface("Problems.IPlugin"); 
       if (typeInterface != null) 
        plugins.Add((IPlugin)Activator.CreateInstance(
            dllAssembly.GetType(type.ToString()))); 
      } 
     } 
    } 
} 

public interface IPlugin 
{ 
    string Name {get; set;} 
    void Work(); 
} 

Затем, у меня есть отдельный узел, MyPlugin.dll, который содержит следующее:

public class MyPlugin : IPlugin 
{ 
    public string Name { get; set; } 
    public void Work() 
    { 
     Console.WriteLine("Hello, World. I'm a plugin!"); 
    } 
} 

Я проверил все это с третьим проектом, TestApp.exe (с Core.dll в качестве эталона):

static void Main(string[] args) 
{ 
    Core core = new Core(@"C:\Problems"); 
    Console.ReadLine(); 
} 

Я поместил оба файла Core.dll и MyPlugin.dll в C: \ Problems, а TestApp.exe работает как шарм! Итак, я пишу этот PowerShell скрипт:

Add-Type -Path "C:\Problems\Core.dll" 
$core = New-Object Problems.Core "C:\Problems" 

Это мой результат (представьте себе кучу красный):

New-Object : Exception calling ".ctor" with "1" argument(s): "Unable to load one or more o 
f the requested types. Retrieve the LoaderExceptions property for more information." 
At line:1 char:11 
+ New-Object <<<< Problems.Core "C:\Problems" 
    + CategoryInfo   : InvalidOperation: (:) [New-Object], MethodInvocationException 
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand 

Бурение вниз в Exception, я нахожу это:

PS C:\Problems> $Error[0].Exception.InnerException.LoaderExceptions 
Could not load file or assembly 'Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified. 

Мое заключение заключается в том, что когда я запускаю GetTypes() как часть загрузчика плагинов, он видит, что MyPlugin наследует от Problems.IPlugin, который он не k теперь, где найти. Но разве я не загрузил его в Core.dll?

Единственный способ, которым я могу обойти это, - подписать сборку Core.dll и добавить ее в GAC, но это и раздражает, и не подходит для моих целей.

(Все классы в пространстве имен проблем, и я опустил тонну очистки и фильтрации логики, чтобы проиллюстрировать эту проблему.)

ответ

2

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

public class Core 
{ 
    List<IPlugin> plugins = new List<IPlugin>(); 
    string path; 

public Core(string path) 
{ 
    this.path = path; 
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); 
    LoadPlugins(); 
    foreach (IPlugin plugin in plugins) 
     plugin.Work(); 
} 

Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) 
{ 
    var currentAssemblies = AppDomain.CurrentDomain.GetAssemblies(); 

    foreach (var assembly in currentAssemblies) 
    { 
     if (assembly.FullName == args.Name) 
     { 
      Console.WriteLine("resolved assembly " + args.Name + " using exising appdomain"); 
      return assembly; 
     } 
    } 

    var file = args.Name.Split(new char[] { ',' })[0].Replace(@"\\", @"\"); 
    var dll = Path.Combine(path, file) + ".dll"; 
    Console.WriteLine("resolved assembly " + args.Name + " from " + path); 
    return File.Exists(dll) ? Assembly.LoadFrom(dll) : null; 
} 

void LoadPlugins() 
{ 
    foreach (string file in Directory.GetFiles(this.path, "*.dll")) 
    { 
     FileInfo fileInfo = new FileInfo(file); 
     Assembly dllAssembly = Assembly.LoadFile(file); 
     foreach (Type type in dllAssembly.GetTypes()) 
     { 
      Type typeInterface = type.GetInterface("Problems.IPlugin"); 
      if (typeInterface != null) 
       plugins.Add((IPlugin)Activator.CreateInstance(
           dllAssembly.GetType(type.ToString()))); 
     } 
    } 
} 

} 
+0

Это работает, и это мой лучший вариант, но для меня это все еще не имеет смысла. Зачем мне перезагружать Core.dll? Я запускаю LoadPlugins() из его экземпляра уже! –

+0

Кроме того, это заставляет меня отслеживать, где находится Core.dll. Моя цель состоит в том, что я включаю только Core.dll один раз, а затем указываю, где находятся плагины. –

+0

Обнаружено обходное решение. Я отредактировал, чтобы завершить свой ответ. У кого-нибудь есть идея, почему это происходит? Не имеет смысла сначала смотреть в текущем AppDomain? Почему это только проблема при запуске из PowerShell? –

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

  • Нет связанных вопросов^_^