.net 在运行时重新加载程序集
.net reload assembly at runtime
我搜索了很多有关在 .NET 中运行时重新加载程序集的信息。我能找到的唯一方法是使用另一个 AppDomain。但这让事情变得非常复杂。在我的情况下这几乎是不可能的,因为将在运行时加载的程序集中的 类 不继承自 MarshalByRefObject。我看过 Unity 游戏引擎。编辑器在运行时构建组件并仅使用已编译的程序集。怎么可能?
一般来说,您不能在同一个 AppDomain 中重新加载程序集。您可以动态创建一个并加载它(它将永远位于您的 AppDomain 中),您可以加载另一个几乎但不完全相同的程序集副本,但是一旦程序集位于 AppDomain 中,它就会卡住。
假设一个库程序集定义了 SomeType,而您的客户端代码刚刚创建了一个实例。如果你卸载库,这个实例应该发生什么?如果该库位于另一个 AppDomain 中,则客户端将使用具有明确定义(在 MarshalByRefObject 中)行为的代理(以卸载域并永远抛出异常)。支持卸载任意类型会使运行时异常复杂、不可预测或两者兼而有之。
关于 Unity,请参阅 this discussion。引用:
An "assembly reaload" sounds like some kind of quick update check but in fact the whole scripting environment reloads. This will destroy everything in the managed land. Unity can recover from this by using it's serialization system. Unity serializes the whole scene before the reload, then recreates everything and deserializing the whold scene. Of course only things which can be serialized will "survive" this process.
我已经使用 MEF 完成了此操作。我不确定它是否适合您,但效果很好。然而,即使使用 MEF,它也有些复杂。
就我而言,我正在从特定文件夹加载所有 dll。
这些是设置 类。
public static class SandBox
{
public static AppDomain CreateSandboxDomain(string name, string path, SecurityZone zone)
{
string fullDirectory = Path.GetFullPath(path);
string cachePath = Path.Combine(fullDirectory, "ShadowCopyCache");
string pluginPath = Path.Combine(fullDirectory, "Plugins");
if (!Directory.Exists(cachePath))
Directory.CreateDirectory(cachePath);
if (!Directory.Exists(pluginPath))
Directory.CreateDirectory(pluginPath);
AppDomainSetup setup = new AppDomainSetup
{
ApplicationBase = fullDirectory,
CachePath = cachePath,
ShadowCopyDirectories = pluginPath,
ShadowCopyFiles = "true"
};
Evidence evidence = new Evidence();
evidence.AddHostEvidence(new Zone(zone));
PermissionSet permissions = SecurityManager.GetStandardSandbox(evidence);
return AppDomain.CreateDomain(name, evidence, setup, permissions);
}
}
public class Runner : MarshalByRefObject
{
private CompositionContainer _container;
private DirectoryCatalog _directoryCatalog;
private readonly AggregateCatalog _catalog = new AggregateCatalog();
public bool CanExport<T>()
{
T result = _container.GetExportedValueOrDefault<T>();
return result != null;
}
public void Recompose()
{
_directoryCatalog.Refresh();
_container.ComposeParts(_directoryCatalog.Parts);
}
public void RunAction(Action codeToExecute)
{
MefBase.Container = _container;
codeToExecute.Invoke();
}
public void CreateMefContainer()
{
RegistrationBuilder regBuilder = new RegistrationBuilder();
string pluginPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
_directoryCatalog = new DirectoryCatalog(pluginPath, regBuilder);
_catalog.Catalogs.Add(_directoryCatalog);
_container = new CompositionContainer(_catalog, true);
_container.ComposeExportedValue(_container);
Console.WriteLine("exports in AppDomain {0}", AppDomain.CurrentDomain.FriendlyName);
}
}
这是实际代码。
AppDomain domain = SandBox.CreateSandboxDomain($"Sandbox Domain_{currentCount}", directoryName, SecurityZone.MyComputer);
foreach (FileInfo dll in currentDlls)
{
string path = Path.GetFullPath(Path.Combine(directoryName, dll.Name));
if (!File.Exists(path))
File.Copy(dll.FullName, Path.Combine(directoryName, dll.Name), true);
domain.Load(typeof(Runner).Assembly.FullName);
}
您可以通过执行此操作取回域。
Runner runner = (Runner) domain.CreateInstanceAndUnwrap(typeof(Runner).Assembly.FullName, typeof(Runner).FullName);
runner.CreateMefContainer(); // or runner.Recompose();
您需要这样调用您的代码。
runner.RunAction(() =>
{
IRepository export = MefBase.Resolve<IRepository>();
export?.Get("123");
Console.WriteLine("Executing {0}", export);
});
我搜索了很多有关在 .NET 中运行时重新加载程序集的信息。我能找到的唯一方法是使用另一个 AppDomain。但这让事情变得非常复杂。在我的情况下这几乎是不可能的,因为将在运行时加载的程序集中的 类 不继承自 MarshalByRefObject。我看过 Unity 游戏引擎。编辑器在运行时构建组件并仅使用已编译的程序集。怎么可能?
一般来说,您不能在同一个 AppDomain 中重新加载程序集。您可以动态创建一个并加载它(它将永远位于您的 AppDomain 中),您可以加载另一个几乎但不完全相同的程序集副本,但是一旦程序集位于 AppDomain 中,它就会卡住。
假设一个库程序集定义了 SomeType,而您的客户端代码刚刚创建了一个实例。如果你卸载库,这个实例应该发生什么?如果该库位于另一个 AppDomain 中,则客户端将使用具有明确定义(在 MarshalByRefObject 中)行为的代理(以卸载域并永远抛出异常)。支持卸载任意类型会使运行时异常复杂、不可预测或两者兼而有之。
关于 Unity,请参阅 this discussion。引用:
An "assembly reaload" sounds like some kind of quick update check but in fact the whole scripting environment reloads. This will destroy everything in the managed land. Unity can recover from this by using it's serialization system. Unity serializes the whole scene before the reload, then recreates everything and deserializing the whold scene. Of course only things which can be serialized will "survive" this process.
我已经使用 MEF 完成了此操作。我不确定它是否适合您,但效果很好。然而,即使使用 MEF,它也有些复杂。
就我而言,我正在从特定文件夹加载所有 dll。
这些是设置 类。
public static class SandBox
{
public static AppDomain CreateSandboxDomain(string name, string path, SecurityZone zone)
{
string fullDirectory = Path.GetFullPath(path);
string cachePath = Path.Combine(fullDirectory, "ShadowCopyCache");
string pluginPath = Path.Combine(fullDirectory, "Plugins");
if (!Directory.Exists(cachePath))
Directory.CreateDirectory(cachePath);
if (!Directory.Exists(pluginPath))
Directory.CreateDirectory(pluginPath);
AppDomainSetup setup = new AppDomainSetup
{
ApplicationBase = fullDirectory,
CachePath = cachePath,
ShadowCopyDirectories = pluginPath,
ShadowCopyFiles = "true"
};
Evidence evidence = new Evidence();
evidence.AddHostEvidence(new Zone(zone));
PermissionSet permissions = SecurityManager.GetStandardSandbox(evidence);
return AppDomain.CreateDomain(name, evidence, setup, permissions);
}
}
public class Runner : MarshalByRefObject
{
private CompositionContainer _container;
private DirectoryCatalog _directoryCatalog;
private readonly AggregateCatalog _catalog = new AggregateCatalog();
public bool CanExport<T>()
{
T result = _container.GetExportedValueOrDefault<T>();
return result != null;
}
public void Recompose()
{
_directoryCatalog.Refresh();
_container.ComposeParts(_directoryCatalog.Parts);
}
public void RunAction(Action codeToExecute)
{
MefBase.Container = _container;
codeToExecute.Invoke();
}
public void CreateMefContainer()
{
RegistrationBuilder regBuilder = new RegistrationBuilder();
string pluginPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
_directoryCatalog = new DirectoryCatalog(pluginPath, regBuilder);
_catalog.Catalogs.Add(_directoryCatalog);
_container = new CompositionContainer(_catalog, true);
_container.ComposeExportedValue(_container);
Console.WriteLine("exports in AppDomain {0}", AppDomain.CurrentDomain.FriendlyName);
}
}
这是实际代码。
AppDomain domain = SandBox.CreateSandboxDomain($"Sandbox Domain_{currentCount}", directoryName, SecurityZone.MyComputer);
foreach (FileInfo dll in currentDlls)
{
string path = Path.GetFullPath(Path.Combine(directoryName, dll.Name));
if (!File.Exists(path))
File.Copy(dll.FullName, Path.Combine(directoryName, dll.Name), true);
domain.Load(typeof(Runner).Assembly.FullName);
}
您可以通过执行此操作取回域。
Runner runner = (Runner) domain.CreateInstanceAndUnwrap(typeof(Runner).Assembly.FullName, typeof(Runner).FullName);
runner.CreateMefContainer(); // or runner.Recompose();
您需要这样调用您的代码。
runner.RunAction(() =>
{
IRepository export = MefBase.Resolve<IRepository>();
export?.Get("123");
Console.WriteLine("Executing {0}", export);
});