我已经研究了几天了。我正试图在外部程序集中的自己的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;
}
}