项目
版本

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 并创建新的作用域,所有程序集元数据都被隔离到最初的“加载上下文作用域”中。

在本文档中