运行时简单的注入器变更注册

本文关键字:注册 注入器 简单 运行时 | 更新日期: 2023-09-27 18:14:19

我使用简单的注入器与ASP。我想为一个接口使用不同的实现,这取决于所使用的REST端点的版本。例如,如果使用端点的v1,则IPaymentData应该用一个称为PaymentData的类实例化,但如果使用v2,则IPaymentData应该由一个称为PaymentDataNew的类实现。事实证明这是有问题的!

我不想像简单注入器文档中建议的那样使用复合类,因为这意味着我的代码需要意识到并考虑到我正在使用的注入框架(坏)。

我注意到在最新版本(3.0)中,有一个叫做"基于上下文的注入"的特性。使用container.RegisterConditional函数,应该可以在每次解析类型时运行一个委托。

container.RegisterConditional(typeof(IPaymentData),
    c => ((HttpContext.Current.Items["version"] as string ?? "1") == "2") 
        ? typeof(PaymentDataNew) 
        : typeof(PaymentData),
    Lifestyle.Scoped,
    c => true);

这似乎不起作用,虽然,因为即使生命周期是有范围的,默认的生活方式是WebApiRequestLifestyle,根据版本返回实现的委托只在第一个请求中调用。随后的请求跳过这个(它们似乎正在使用缓存实现)。

我错过了什么吗?我如何确保每次请求进来时都调用委托?

运行时简单的注入器变更注册

因为这意味着我的代码需要意识到并考虑到我正在使用的注入框架

不完全是。复合模式是一种众所周知且常用的模式,因此在代码中定义它不会使代码依赖于DI库。如果不想在应用程序代码中定义复合,可以在Composition根目录中指定它。复合根已经非常依赖于DI库,所以任何特定于库的东西都应该放在那里。

使用容器。RegisterConditional函数,应该可以在每次解析类型时运行一个委托。

Simple Injector v3的RegisterConditional方法允许基于静态信息有条件地应用注册。这意味着所提供的谓词只在有限的时间内被调用(通常每个消费组件调用一次)。方法(IntelliSense/XML)文档说明:

谓词只能求值有限次;谓词不适合基于运行时条件做出决策。

它将而不是在每次解析时被调用。这样做的原因有两个:

    这可以优化性能,因为在谓词中做出的决定可以写入编译后的表达式,但更重要的是,
  1. 这可以防止您在构建对象图期间做出运行时决策。

对象图的形状不应该依赖于运行时参数(例如HttpContext中的version)。这样做会使对象图变得复杂,并且使验证和诊断对象图的正确性变得非常困难。

我建议的解决方案是为IPaymentData实现一个代理实现,允许在运行时进行切换。这是而不是一个特定于简单注入器的实现;你应该努力拥有简单、静态和可验证的对象图,独立于你使用的DI库。

这样的代理应该是这样的:

public sealed class HttpRequestVersionSelectorPaymentData : IPaymentData
{
    private readonly PaymentData paymentOld;
    private readonly PaymentDataNew paymentNew;
    public VersionSelectorPaymentData(PaymentData old, PaymentDataNew paymentNew) { ... }
    private bool New => HttpContext.Current.Items["version"] as string ?? "1") == "2";
    private IPaymentData PaymentData => this.New ? paymentNew : paymentOld;
    // IPaymentData method(s)
    public Payment GetData(int id) => this.PaymentData.GetData(id);
}

尽管在构建对象图期间(通过注册委托)绝对有可能做出运行时决策,但我强烈建议不要这样做,原因如上所述。