在新的AppDomain下执行dll程序集



我已经研究了几天了。我正试图在外部程序集中的自己的AppDomain下正确地创建一个类的实例。

我已经能够在新的AppDomain下加载外部程序集,但它的所有依赖项似乎都加载在父AppDomain中,这违背了目的,因为我想稍后卸载dll以释放对它们的锁定(插件系统)。你知道它为什么要这么做吗?

public MyCustomObject BindAssembly()
{
    string currentAssemblyPath = @"C:PathToMyAssembly";
    string currentAssemblyFile = @"C:PathToMyAssemblyMyAssembly.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;
        }
    }
}

我不确定这(或通常跨应用程序域)将如何与抽象类的不同实现一起工作,因为您可能会发现在主AppDomain中使用子类仍然需要依赖项,但我最近不得不做类似的事情,发现使用与MyCustomObject在同一程序集中的Serializable Loader类,是把事情分开的最好方法。

其思想是在当前AppDomain中创建Loader,然后将其编组到新的AppDomain,并要求在那里加载和实例化所需的程序集和类型。

请注意,Loader和从MyCustomObject继承的任何东西都必须是Serializable或从MarshalByRefObject继承,因为它们将在AppDomain的之间传递

public MyCustomObject BindAssembly()
{
    string currentAssemblyPath = @"C:PathToMyAssembly";
    string currentAssemblyFile = @"C:PathToMyAssemblyMyAssembly.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;
    }
}