我正在使用简单注入(inject)器。我有一个从一开始就使用 DI 的后台处理器。它将拾取要运行的作业并运行它们。但是,每个作业都需要在自己的范围内运行,以便我可以覆盖一些上下文依赖项。例如,作业需要在特定的安全上下文(创建它的安全上下文)中运行,因此我需要启动一个新作用域并覆盖 ISecurityContext
注入(inject),以便正确保护作业.
为了实现这一目标,我创建了一个新容器(使用正确的 ISecurityContext
)并启动一个范围,然后运行该作业,但我不确定这是否合适.
运行作业
private readonly Func<ISecurityContext, Container> _containerFactory;
internal async Task RunJob(BackgroundJob job) {
var parameters = job.GetParameters();
var securityContext = parameters.SecurityContext;
using (var container = _containerFactory(securityContext))
using (AsyncScopedLifestyle.BeginScope(container)) {
// Run the job within this scope.
}
}
DI 位
container.RegisterSingleton<Func<ISecurityContext, Container>>(() => securityContext => {
var c = new Container();
RegisterDependencies(c);
c.Options.AllowOverridingRegistrations = true;
c.Register<ISecurityContext>(() => securityContext, Lifestyle.Scoped);
return c;
});
我感觉不对,但我不确定正确的解决方案是什么。
最佳答案
简单注入(inject)器文档 warns关于您正在做的事情,请说明:
Warning: Do not create an infinite number of Container instances (such as one instance per request). Doing so will drain the performance of your application. The library is optimized for using a very limited number of Container instances. Creating and initializing Container instances has a large overhead, but resolving from the Container is extremely fast once initialized.
一般来说,您应该为每个应用程序仅创建一个 Container
实例。这不仅从性能角度来看是成立的,而且这种“子容器”的创建通常充满了怪癖和缺陷。例如,如何确保整个应用程序中的注册是单例的?
因此,不要滥用容器来实现运行时状态,而应将其存储在其他地方。您可以使用 Scope
实例作为作用域状态的字典,但为注册为 Scoped
实例的 ISecurityContext
创建一个简单的包装器也很容易并在创建作用域后直接进行初始化,如以下示例所示。
// Can be part of your Composition Root
internal sealed class SecurityContextWrapper : ISecurityContext
{
// One of the rare cases that Property Injection makes sense.
public ISecurityContext WrappedSecurityContext { get; set; }
// Implement ISecurityContext methods here that delegate to WrappedSecurityContext.
}
// Composition Root. Only have 1 container for the complete application
c = new Container();
RegisterDependencies(c);
c.Register<SecurityContextWrapper>(Lifestyle.Scoped);
c.Register<ISecurityContext, SecurityContextWrapper>(Lifestyle.Scoped);
// Job logic
private readonly Container _container;
internal async Task RunJob(BackgroundJob job) {
var parameters = job.GetParameters();
var securityContext = parameters.SecurityContext;
using (AsyncScopedLifestyle.BeginScope(_container)) {
// Resolve the wapper inside the scope
var wrapper = _container.GetInstance<SecurityContextWrapper>();
// Set it's wrapped value.
wrapper.WrappedSecurityContext = securityContext;
// Run the job within this scope.
}
}
或者,如果您使用 Scope
作为状态,则可以注入(inject) Scope
实例作为 SecurityContextWrapper
的构造函数参数。这消除了使用属性注入(inject)的需要,但确实使您的 SecurityContextWrapper
依赖于简单注入(inject)器:
// Can be part of your Composition Root
internal sealed class SecurityContextWrapper : ISecurityContext
{
ISecurityContext _wrappedSecurityContext;
public SecurityContextWrapper(Scope scope)
{
_wrappedSecurityContext= (ISecurityContext)scope.GetItem(typeof(ISecurityContext));
}
// Implement ISecurityContext methods here that delegate to WrappedSecurityContext.
}
// Composition Root. Only have 1 container for the complete application
c = new Container();
RegisterDependencies(c);
c.Register<ISecurityContext, SecurityContextWrapper>(Lifestyle.Scoped);
// Job logic
private readonly Container _container;
internal async Task RunJob(BackgroundJob job) {
var parameters = job.GetParameters();
var securityContext = parameters.SecurityContext;
using (var scope = AsyncScopedLifestyle.BeginScope(_container)) {
// Set it's wrapped value.
scope.SetItem(typeof(ISecurityContext), securityContext);
// Run the job within this scope.
}
}
关于c# - 覆盖范围内依赖关系的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44015829/