我使用Simple Injector和ASP.NET Web API项目,我想对一个接口使用不同的实现,具体取决于所使用的REST端点的版本。例如,如果使用端点的v1,则IPaymentData
应使用名为PaymentData
的类进行实例化,但如果使用v2 IPaymentData
应由名为{{1}的类实现}。结果证明这是有问题的!
我不想使用类似Simple Injector文档的一部分的复合类,因为这意味着我的代码需要注意并考虑我使用的注入框架(坏)
我注意到在最新版本(3.0)中,名为"Context based injection"的内容是一项功能。使用PaymentDataNew
函数,每次解析类型时都应该可以运行委托。
container.RegisterConditional
这似乎不起作用,因为即使生命周期是作用域,默认生活方式是container.RegisterConditional(typeof(IPaymentData),
c => ((HttpContext.Current.Items["version"] as string ?? "1") == "2")
? typeof(PaymentDataNew)
: typeof(PaymentData),
Lifestyle.Scoped,
c => true);
,根据版本返回实现的委托只调用第一个请求进来。后续请求跳过此(他们似乎使用缓存实现)。
我有什么遗失的东西吗?如何在每次请求进入时确保调用委托?
答案 0 :(得分:6)
因为这意味着我的代码需要注意并考虑我正在使用的注入框架
不完全是。复合模式是众所周知且常用的模式,因此在代码中定义它不会使您的代码依赖于DI库。如果您不想在应用程序代码中定义组合,则可以始终在Composition Root中指定组合。 Composition Root已经非常依赖于DI库,所以任何特定于库的东西都应放在那里。
使用container.RegisterConditional函数,每次解析类型时都应该可以运行委托。
Simple Injector v3的RegisterConditional
方法允许根据静态信息有条件地应用注册。这意味着所提供的谓词仅被称为有限的时间量(通常每个消耗组件一次)。方法(IntelliSense / XML)文档states:
谓词只会被评估有限次数;谓词不适合根据运行时条件做出决定。
每次解决时都会调用 。原因有两个:
对象图的形状不应取决于运行时参数(例如version
中的HttpContext
)。这样做会使对象图变得复杂,并且使对象图的verify和diagnose变得非常困难。
我建议的解决方案是为IPaymentData
实现一个允许在运行时进行切换的代理实现。这是不是 Simple Injector特定的实现;你应该努力拥有简单,静态和可验证的对象图,而不管你使用的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);
}
虽然绝对有可能在构建对象图期间做出运行时决策(通过注册委托),但我强烈建议不要这样做,原因如上所述。