C# 运行时简单的喷油器更改注册

C# 运行时简单的喷油器更改注册,c#,asp.net-web-api,dependency-injection,simple-injector,C#,Asp.net Web Api,Dependency Injection,Simple Injector,我在ASP.NET Web API项目中使用Simple Injector,我希望根据所使用的REST端点版本,为一个接口使用不同的实现。例如,如果使用端点的v1,则应使用名为PaymentData的类实例化IPaymentData,但如果使用v2,则应使用名为PaymentDataNew的类来实现IPaymentData。这是有问题的 我不想像Simple Injector文档的一部分那样使用复合类,因为这意味着我的代码需要注意并考虑我正在使用的注入框架(糟糕) 我注意到在最新版本(3.0)中

我在ASP.NET Web API项目中使用Simple Injector,我希望根据所使用的REST端点版本,为一个接口使用不同的实现。例如,如果使用端点的v1,则应使用名为
PaymentData
的类实例化
IPaymentData
,但如果使用v2,则应使用名为
PaymentDataNew
的类来实现
IPaymentData
。这是有问题的

我不想像Simple Injector文档的一部分那样使用复合类,因为这意味着我的代码需要注意并考虑我正在使用的注入框架(糟糕)

我注意到在最新版本(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);
但是,这似乎不起作用,因为即使生命周期是有范围的,并且默认的生活方式是
webapirequestlifety
,根据版本返回实现的委托也只会为传入的第一个请求调用。后续请求跳过了这一点(它们似乎使用了缓存实现)

有什么我遗漏的吗?如何确保每次收到请求时都调用代理

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

不完全是。复合模式是一种众所周知且常用的模式,因此在代码中定义它不会使代码依赖于DI库。如果您不想在应用程序代码中定义组合,您可以在应用程序代码中指定它。组合根已经对DI库有很强的依赖性,因此任何特定于库的内容都应该放在那里

使用container.RegisterCondition函数,应该可以在每次解析类型时运行委托

container.RegisterConditional(typeof(IPaymentData),
    c => ((HttpContext.Current.Items["version"] as string ?? "1") == "2") 
        ? typeof(PaymentDataNew) 
        : typeof(PaymentData),
    Lifestyle.Scoped,
    c => true);
Simple Injector v3的
RegisterCondition
方法允许基于静态信息有条件地应用注册。这意味着提供的谓词只在有限的时间内调用(通常每个消费组件调用一次)。方法(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);
    }
    
    尽管在构建对象图的过程中(通过注册代理)完全可以做出运行时决策,但出于上述原因,我强烈建议不要这样做。

    谢谢,@Steven:)。我对你优秀的依赖注入器的理解是初步的,但正在逐步提高。文档也很好!!