AssemblyLoadContext 与生命周期范围
在 .NET Core 中,引入了 AssemblyLoadContext ,使得开发者能够动态地从应用程序加载和卸载程序集。这对于编写基于插件架构的应用程序的开发者来说非常有用。
为了在程序集不再需要时从 AssemblyLoadContext
卸载程序集,外部不能有任何对该程序集中类型的引用。这包括 Autofac,默认情况下,Autofac 会为已注册的类型保留多种内部引用和缓存。
自 Autofac 7.0 起,我们添加了支持,用于向 Autofac 指示给定生命周期范围表示为特定 AssemblyLoadContext
加载的类型;当为特定 AssemblyLoadContext
创建的生命周期范围被卸载时,Autofac 将尽最大努力尝试移除我们为加载上下文中类型所持有的所有引用,以便能够卸载 AssemblyLoadContext
。
您可以通过新的 BeginLoadContextLifetimeScope
方法来指示生命周期范围是针对 AssemblyLoadContext
的。下面是一个完整的示例:
// 在 PluginDefinition 项目中。
public interface IPlugin
{
void DoSomething();
}
// 在 MyPlugin 项目中。
public class MyPlugin : IPlugin
{
public void DoSomething()
{
Console.WriteLine("Hello World");
}
}
// 在应用程序中。
var builder = new ContainerBuilder();
// 在“默认”加载上下文中定义的组件将在加载上下文生命周期范围内可用。
builder.RegisterType<DefaultComponent>();
var container = builder.Build();
var loadContext = new AssemblyLoadContext("PluginContext", isCollectible: true);
using (var scope = container.BeginLoadContextLifetimeScope(loadContext, builder =>
{
var pluginAssembly = loadContext.LoadFromAssemblyPath("plugins/MyPlugin.dll");
builder.RegisterAssemblyTypes(pluginAssembly).AsImplementedInterfaces();
}))
{
// 应用程序应引用 PluginDefinition 项目,这意味着
// 默认加载上下文已经加载了 IPlugin 接口。当 MyPlugin 程序集被加载时,
// 它应该共享相同的类型,并允许通过公共接口进行解析。
var plugin = scope.Resolve<IPlugin>();
plugin.DoSomething();
}
loadContext.Unload();
注意:
如果您在 Autofac 之外捕获任何已解析组件或加载程序集中的任何类型的引用,很可能将无法卸载您的加载上下文。
无论是否使用 Autofac,要确保每次都能卸载AssemblyLoadContext
都很复杂。如果您遇到问题,请参阅 dotnet 文档中关于 解决卸载性问题 的部分。
您可以使用常规的 BeginLifetimeScope
方法从“加载上下文范围”创建额外的生命周期范围,而无需进一步跟踪加载上下文。
这意味着您可以加载一个插件,然后插件可以解析 ILifetimeScope
并创建新的作用域,所有程序集元数据都被隔离到最初的“加载上下文作用域”中。