运行时简单的注入器变更注册
本文关键字:注册 注入器 简单 运行时 | 更新日期: 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)文档说明:
谓词只能求值有限次;谓词不适合基于运行时条件做出决策。
它将而不是在每次解析时被调用。这样做的原因有两个:
- 这可以优化性能,因为在谓词中做出的决定可以写入编译后的表达式,但更重要的是,
- 这可以防止您在构建对象图期间做出运行时决策。
对象图的形状不应该依赖于运行时参数(例如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);
}
尽管在构建对象图期间(通过注册委托)绝对有可能做出运行时决策,但我强烈建议不要这样做,原因如上所述。