使用 Autofac 将 IServiceProvider 注入工厂 Class

Injecting IServiceProvider into Factory Class with Autofac

我在 Net Core 3 控制台应用程序中有一个工厂 class,它需要能够在运行时针对 DI 容器进行解析:

public class OptionFactory : IOptionFactory
{
    private readonly IServiceProvider _svcProvider;

    public OptionFactory( IServiceProvider svcProvider )
    {
        _svcProvider = svcProvider;
    }

    public IOption<T>? CreateOption<T>( params string[] keys )
    {
        // code eliminated for brevity
        try
        {
            return retVal = _svcProvider.GetRequiredService<Option<T>>();
        }
        catch( Exception e )
        {
            return null;
        }
    }
}

我正在使用 Autofac 来定义 DI 容器,然后 "assign" 它通过 new AutofacServiceProvider( builder.Build() ) 在供应商中 new AutofacServiceProvider( builder.Build() )IServiceProvider class:

public class TestServiceProvider 
{
    public static IServiceProvider Instance { get; private set; }

    static TestServiceProvider()
    {
        var builder = new ContainerBuilder();

        builder.RegisterType<OptionFactory>()
            .As<IOptionFactory>()
            .SingleInstance();

        // code omitted for brevity
        Instance = new AutofacServiceProvider( builder.Build() );
    }
}

我不清楚如何将 IServiceProvider 本身注册到 DI 容器,以便它可以注入到构造函数中。这可能吗?好像有点自我参照,可能会有问题。

我在网上看到的所有示例都要求引用回 Autofac IContainer 本身(或者在我的示例中引用 TestServiceProvider.Instance)。我可以这样做,但最终会将我的图书馆绑定到具体的服务提供商 class。如果可以,我想避免这种情况。

我意识到注入 IServiceProvider 被 some/many 认为是一种反模式,尽管其他人认为它在工厂 class 中是可以接受的,因为工厂正在 "simply" 扩展去离子容器。我对不依赖工厂的其他方法持开放态度 class,前提是它们允许我在运行时创建开放泛型类型的具体实例。

你有几个选择(没有双关语意)。

最简单:使用空集合调用 builder.Populate()

Autofac.Extensions.DependencyInjection 包(您正在使用,因为您有 AutofacServiceProvider)有一个扩展方法 ContainerBuilder.Populate() which handles registering stuff from an IServiceCollection and auto-registering the AutofacServiceProvider。您可以使用空服务集合调用该方法,它会起作用。

builder.Populate(Enumerable.Empty<ServiceDescriptor>());

这将为您提供您正在寻找的东西。但是,还有一个替代方案可以考虑...

备选方案:使用 ILifetimeScope

如果你的 OptionFactory 是否与 Autofac 绑定无关紧要,你可以注入 ILifetimeScope。 Autofac 已自动注册当前生命周期范围,因此这将起作用:

public OptionFactory(ILifetimeScope scope)
{
  // scope is whatever lifetime scope the
  // factory itself came from - if that's the
  // root container, then the scope is the
  // container
}

这里的好处是您将获得 Autofac 提供的更丰富的解析选项,而无需任何额外的工作。缺点是您在这个级别与 Autofac 绑定,这可能重要也可能不重要。

小心!

这可能只是您的示例,但如果您按照示例所示的方式直接从根容器解析,则有一些重要信息需要了解:

你很容易会导致严重的内存泄漏。

Autofac 保留它解析的所有 IDisposable 个实例,以便在处理生命周期范围时可以安全地处理它们。如果您从容器中解析,这意味着任何 IDisposable 都将被保留,直到容器本身被释放,对于大多数人来说,这是应用程序的生命周期。这意味着 - 假设 - 每个解决方案都可能只添加一点点内存,直到容器被处理掉才会被处理掉。内存泄漏。

出于这个原因,我们建议 always resolving from a nested lifetime scope 而不是从容器中。在 Web 应用程序中,请求级别的生命周期范围是完美的,因为它会在请求后消失。在这样的示例中,由您和您的应用程序代码来确定集成生命周期范围的最佳方式。

当然,如果您 肯定会 100% 保证 永远不会解决任何问题 IDisposable,不用担心。