使用 Autofac 将 IServiceProvider 注入工厂类

Mar*_*ert 0 c# dependency-injection autofac

我在 Net Core 3 控制台应用程序中有一个工厂类,它需要能够在运行时针对 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我使用 Autofac 定义 DI 容器,然后将其“分配”给提供程序类中的IServiceProvidervia :new AutofacServiceProvider( builder.Build() )

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() );
    }
}
Run Code Online (Sandbox Code Playgroud)

我不清楚如何IServiceProvider向 DI 容器注册自身,以便将其注入到构造函数中。这可能吗?这似乎有点自我指涉,这可能会有问题。

我在网上看到的所有示例都要求引用 AutofacIContainer本身(或TestServiceProvider.Instance在我的示例中)。我可以做到这一点,但最终会将我的库与具体的服务提供者类联系起来。我想如果可以的话我想避免这种情况。

我意识到注入IServiceProvider被一些/许多人认为是一种反模式,尽管其他人认为它在工厂类中是可以接受的,因为工厂“简单地”扩展了 DI 容器。我对不依赖工厂类的其他方法持开放态度,只要它们允许我在运行时创建开放泛型类型的具体实例。

Tra*_*lig 5

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

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

Autofac.Extensions.DependencyInjection包(您正在使用的包,因为您有AutofacServiceProvider)有一个扩展方法ContainerBuilder.Populate() ,该方法可以处理从 注册内容IServiceCollection并自动注册AutofacServiceProvider. 您可以使用空服务集合调用该方法,它会起作用。

builder.Populate(Enumerable.Empty<ServiceDescriptor>());
Run Code Online (Sandbox Code Playgroud)

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

替代方案:使用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
}
Run Code Online (Sandbox Code Playgroud)

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

谨防!

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

你很容易就会出现严重的内存泄漏。

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

因此,我们建议始终从嵌套的生命周期范围而不是从容器进行解析。在网络应用程序中,请求级生命周期范围是完美的,因为它在请求后消失。在这样的示例中,由您和您的应用程序代码来确定集成生命周期范围的最佳方法。

当然,如果你百分百保证永远不会解决任何问题IDisposable,那就不用担心。