简体   繁体   English

使用 Autofac 将 IServiceProvider 注入工厂 Class

[英]Injecting IServiceProvider into Factory Class with Autofac

I have a factory class in a Net Core 3 console app which needs to be able to resolve against a DI container at runtime:我在 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;
        }
    }
}

I'm using Autofac to define the DI container and then "assign" it to IServiceProvider via new AutofacServiceProvider( builder.Build() ) in a provider class:我正在使用 Autofac 定义 DI 容器,然后通过提供者 class 中的new AutofacServiceProvider( builder.Build() )将其“分配”给IServiceProvider

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() );
    }
}

I'm unclear about how to register IServiceProvider itself with the DI container so that it can be injected into the constructor.我不清楚如何将IServiceProvider本身注册到 DI 容器中,以便可以将其注入到构造函数中。 Is that even possible?这甚至可能吗? It seems a little self-referential, which could be problematic.这似乎有点自我参照,这可能是有问题的。

All the examples I've seen online call for referencing back to the Autofac IContainer itself, (or to TestServiceProvider.Instance in my example).我在网上看到的所有示例都要求引用 Autofac IContainer本身(或在我的示例中为TestServiceProvider.Instance )。 I can do that, but it would end tie my library to a concrete service provider class.我可以这样做,但它会将我的图书馆与具体的服务提供商 class 联系起来。 Which I think I'd like to avoid if I can.如果可以的话,我想我想避免。

I realize injecting IServiceProvider is considered an anti-pattern by some/many, although others deem it acceptable in a factory class because the factory is "simply" extending the DI container.我意识到注入IServiceProvider被一些/许多人认为是一种反模式,尽管其他人认为它在工厂 class 中是可以接受的,因为工厂“只是”扩展了 DI 容器。 I'm open to other approaches which don't rely on a factory class, provided they allow me to create concrete instances of open generic types at runtime.我对不依赖工厂 class 的其他方法持开放态度,只要它们允许我在运行时创建开放泛型类型的具体实例。

You have a couple of options (no pun intended ).你有几个选择(没有双关语)。

Easiest: Call builder.Populate() with an empty collection最简单:使用空集合调用builder.Populate()

The Autofac.Extensions.DependencyInjection package (which you're using, since you have AutofacServiceProvider ) has an extension method ContainerBuilder.Populate() which handles registering stuff from an IServiceCollection and auto-registering the AutofacServiceProvider . Autofac.Extensions.DependencyInjection package (您正在使用,因为您有AutofacServiceProvider )有一个扩展方法ContainerBuilder.Populate() 处理从IServiceCollection注册内容和自动注册AutofacServiceProvider You could call that method with an empty service collection and it'll work.您可以使用空的服务集合调用该方法,它会起作用。

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

This will get you exactly the thing you're looking for.这将为您提供您正在寻找的东西。 However, there's an alternative to consider...但是,有一个替代方案可以考虑...

Alternative: Use ILifetimeScope替代方案:使用ILifetimeScope

If it doesn't matter whether your OptionFactory is tied to Autofac, you can inject ILifetimeScope .如果您的OptionFactory是否与 Autofac 绑定无关紧要,您可以注入ILifetimeScope Autofac has the current lifetime scope auto-registered, so this will work: Autofac 具有自动注册的当前生命周期 scope ,因此这将起作用:

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
}

The benefit here is you'll get the richer resolve options Autofac offers without any extra work.这里的好处是您将获得 Autofac 提供的更丰富的解析选项,而无需任何额外的工作。 The drawback would be you're tied to Autofac at this level, which may or may not matter.缺点是您在此级别上与 Autofac 相关联,这可能重要也可能不重要。

Beware!谨防!

It may just be your example, but there's something important to know if you're resolving directly from the root container the way the example shows:这可能只是您的示例,但是如果您按照示例显示的方式直接从根容器解析,则有一些重要的事情要知道:

You could easily end up with a big memory leak.您很容易以大 memory 泄漏告终。

Autofac holds on to all IDisposable instances it resolves so they can be safely disposed when the lifetime scope is disposed. Autofac 保留它解析的所有IDisposable实例,因此当生命周期 scope 被释放时,它们可以被安全地释放。 If you are resolving from the container, that means any IDisposable will be held onto until the container itself is disposed, which, for most, is the lifetime of the application.如果您从容器中解析,这意味着任何IDisposable都将被保留,直到容器本身被释放,对于大多数情况来说,这就是应用程序的生命周期。 That means - hypothetically - every resolution could be adding just a tiny little bit of memory that won't be disposed until the container is disposed.这意味着 - 假设 - 每个解决方案都可能只添加一点点 memory,直到容器被处置后才会被处置。 Memory leak. Memory 泄漏。

For this reason we recommend always resolving from a nested lifetime scope rather than from the container.出于这个原因,我们建议始终从嵌套生命周期 scope而不是从容器中解析。 In a web app, that request-level lifetime scope is perfect because it disappears after a request.在 web 应用程序中,请求级生命周期 scope 是完美的,因为它在请求后消失。 In an example like this, it's up to you and your app code to determine the best way to integrate lifetime scopes.在这样的示例中,由您和您的应用程序代码来确定集成生命周期范围的最佳方式。

And, of course, if you're definitely, 100% guaranteed never resolving anything IDisposable , no worries.而且,当然,如果您确定,100% 保证永远不会解决任何IDisposable问题,不用担心。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM