c# - 覆盖范围内依赖关系的正确方法

标签 c# dependency-injection simple-injector

我正在使用简单注入(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/

相关文章:

c# - 如何在 XML 序列化中插入 XML 注释?

c# - Autofac:解析同名注册的 IEnumerable

php - 如何从类工厂方法添加 Symfony 依赖注入(inject)容器构建器的定义?

c# - 生产代码中的简单注入(inject)器验证()

c# - wpf 绑定(bind)不更新

c# - C# 中的优化技术

c# - 使用 C# 的 Microsoft 媒体基础转换 (MFT)?

.net - 动态代理生成速度

asp.net - 简单注入(inject)器 - 解析 MVC Controller 时没有为此对象定义无参数构造函数

c# - 带有附加依赖的简单注入(inject)器装饰