2014-09-26 3 views
2

Я изучал это в течение нескольких дней. Я пытаюсь правильно создать экземпляр класса внутри внешней сборки под собственным AppDomain.Выполнение сборки dll при новом AppDomain

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

public MyCustomObject BindAssembly() 
{ 
    string currentAssemblyPath = @"C:\PathToMy\Assembly"; 
    string currentAssemblyFile = @"C:\PathToMy\Assembly\MyAssembly.dll"; 
    AssemblyName currentAssemblyName = AssemblyName.GetAssemblyName(currentAssemblyFile); 
    AppDomain domain = AppDomain.CurrentDomain; 
    domain.AssemblyResolve += domain_AssemblyResolve; 
    AppDomainSetup setup = new AppDomainSetup() 
    { 
     PrivateBinPath = currentAssemblyPath, 
     ApplicationBase = domain.BaseDirectory, 
     DynamicBase = domain.SetupInformation.DynamicBase, 
     ShadowCopyFiles = domain.SetupInformation.ShadowCopyFiles, 
     CachePath = domain.SetupInformation.CachePath, 
     AppDomainManagerAssembly = domain.SetupInformation.AppDomainManagerAssembly, 
     AppDomainManagerType = domain.SetupInformation.AppDomainManagerType 
    }; 
    AppDomain newDomain = AppDomain.CreateDomain("NewDomain", AppDomain.CurrentDomain.Evidence, setup); 
    newDomain.AssemblyResolve += newDomain_AssemblyResolve; 

    currentAssembly = newDomain.Load(currentAssemblyName); 
    // tried this too 
    //var obj = domain.CreateInstanceFromAndUnwrap(currentAssemblyFile, className); 

    // list all of the assemblies inside the custom app domain 
    var newDomainAssemblies = newDomain.GetAssemblies(); // (contains my loaded assembly, but not dependencies) 

    // list all of the assemblies inside the parent app domain 
    var appAssemblies = AppDomain.CurrentDomain.GetAssemblies(); // (contains my loaded assembly as well as all dependencies) 

    return obj as MyCustomObject; 
} 

// resolve dependencies in custom domain 
Assembly newDomain_AssemblyResolve(object sender, ResolveEventArgs args) 
{ 
    // this never fires 
    AppDomain domain = (AppDomain)sender; 
} 

// resolve dependencies in parent domain 
Assembly domain_AssemblyResolve(object sender, ResolveEventArgs args) 
{ 
    AppDomain domain = (AppDomain)sender; 
    string assemblyDependencyPath = String.Format(@"{0}", Path.GetDirectoryName(Uri.UnescapeDataString(new UriBuilder(args.RequestingAssembly.CodeBase).Path))); 
    string[] files = Directory.GetFiles(assemblyDependencyPath, "*.dll", SearchOption.AllDirectories); 
    foreach (string file in files) 
    { 
     // if we found it, load the assembly and cache it. 

     AssemblyName aname = AssemblyName.GetAssemblyName(file); 
     if (aname.FullName.Equals(args.Name)) 
     { 
      Assembly assembly = domain.Load(aname); 
      //Assembly assembly = Assembly.LoadFrom(file); // also tried this, same result 
      return assembly; 
     } 
    } 
} 
+0

вы пробовали 'currentAssembly = newDomain.Load (currentAssemblyName);' вместо 'currentAssembly = domain.Load (currentAssemblyName);'? – Rhumborl

+0

Я также подтвердил, что если я выгружаю новый AppDomain, который я создал, зависимые сборки все еще остаются в родительском AppDomain. –

+0

Rhumborl - Извиняюсь, что это была опечатка в моем коде заглушки. Исправлено в моем вопросе. –

ответ

1

Я не знаю, как это (или кросс-AppDomain в целом) будет работать с различными реализациями абстрактного класса, потому что вы можете найти зависимостей все еще требуется использовать класс ребенка в ваш главный AppDomain , но в последнее время мне пришлось сделать аналогичную вещь и обнаружили, что использование класса Serializable Loader, который находится в той же сборке, что и MyCustomObject, является лучшим способом сохранить ситуацию в отдельности.

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

Обратите внимание, что оба погрузчика и ничего унаследовать от MyCustomObject должны быть Serializable или наследовать от MarshalByRefObject, как они будут переданы между AppDomain s

public MyCustomObject BindAssembly() 
{ 
    string currentAssemblyPath = @"C:\PathToMy\Assembly"; 
    string currentAssemblyFile = @"C:\PathToMy\Assembly\MyAssembly.dll"; 
    AssemblyName currentAssemblyName = AssemblyName.GetAssemblyName(currentAssemblyFile); 
    AppDomain domain = AppDomain.CurrentDomain; 
    domain.AssemblyResolve += domain_AssemblyResolve; 
    AppDomainSetup setup = new AppDomainSetup() 
    { 
     PrivateBinPath = currentAssemblyPath, 
     ApplicationBase = domain.BaseDirectory, 
     DynamicBase = domain.SetupInformation.DynamicBase, 
     ShadowCopyFiles = domain.SetupInformation.ShadowCopyFiles, 
     CachePath = domain.SetupInformation.CachePath, 
     AppDomainManagerAssembly = domain.SetupInformation.AppDomainManagerAssembly, 
     AppDomainManagerType = domain.SetupInformation.AppDomainManagerType 
    }; 

    AppDomain newDomain = AppDomain.CreateDomain("NewDomain", AppDomain.CurrentDomain.Evidence, setup); 

    newDomain.Load(typeof(Loader).Assembly.GetName()); 

    Loader loader = (Loader)newDomain.CreateInstanceAndUnwrap(
     typeof(Loader).Assembly.FullName, typeof(Loader).FullName); 

    // load the assembly containing MyCustomObject into the remote domain 
    loader.LoadAssembly(currentAssemblyFile); 

    // ask the Loader to create the object instance for us 
    MyCustomObject obj = loader.CreateCustomObject(); 

    return obj; 
} 

public class Loader : MarshalByRefObject 
{ 
    /// <summary>Stores the assembly containing the task class.</summary> 
    private Assembly assembly; 

    /// <summary>Retrieves the current lifetime service object that controls the lifetime policy for this instance.</summary> 
    /// <returns>This always returns null.</returns> 
    public override object InitializeLifetimeService() 
    { 
     return null; 
    } 

    /// <summary>Loads the assembly containing the task class.</summary> 
    /// <param name="path">The full path to the assembly DLL containing the task class.</param> 
    public void LoadAssembly(string path) 
    { 
     this.assembly = Assembly.Load(AssemblyName.GetAssemblyName(path)); 
    } 

    /// <summary>Instantiates the required object.</summary> 
    /// <param name="classFullName">The full name (namespace + class name) of the task class.</param> 
    /// <returns>The new object.</returns> 
    public MyCustomObject CreateCustomObject() 
    { 
     MyCustomObject instance = new MyCustomObject(); 
     // do whatever you want with the instance here 
     return instance; 
    } 
} 
+0

Я попробовал что-то похожее на это с помощью прокси-сервера, однако я переработал его, чтобы он соответствовал вашему предложению, но он терпит неудачу здесь с невозможностью загрузить ошибка сборки: Loader loader = (Loader) domain.CreateInstanceFromAndUnwrap (typeof (Loader) .Assembly.FullName, typeof (Loader) .FullName); –

+0

после некоторого ворчания я понял, что мне пришлось передать CreateInstanceFromAndUnwrap() полный путь к dll, однако по-прежнему существует поведение, связанное с добавлением зависимостей в родительский AppDomain. –

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