使用 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
,不用担心。
我在 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
,不用担心。