首页 > 解决方案 > 运行时的条件依赖解析器(.net Core)

问题描述

我有两个类PaymentGatewayFooPaymentGatewayBoo它们都实现了一个通用接口IPaymentGateway

interface IPaymentGateway { }

class PaymentGatewayFoo : IPaymentGateway { }
class PaymentGatewayBoo : IPaymentGateway { }

客户端请求有一个标识符,该标识符决定使用哪个实现:

public class OrderService
{
    private readonly IPaymentGateway _service;

    public void DoOrder(bool isFoo)
    {
        if (isFoo)
            //this._service should be resolved with PaymentGatewayFoo
        else
            //this._service should be resolved with PaymentGatewayBoo

        this._service.Pay();
    }
}

如何根据客户在运行时的请求解决正确的实现?

这个问题不是重复的,它是相似的,但它是关于两个独立的控制器(即使答案表明代码甚至不需要条件依赖注入),在我的情况下,基于客户端属性的运行时需要条件依赖价值。

标签: c#dependency-injection.net-core

解决方案


这里有几个选项,但对我来说最明显的两个是使用工厂模式或适配器模式。

1.使用工厂

public class OrderService
{
    private readonly IPaymentGatewayFactory _factory;

    public void DoOrder(bool isFoo)
    {
        IPaymentGateway service = _factory.Create(isFoo);
        this._service.Pay();
    }
}

工厂可以在哪里:

public class PaymentGatewayFactory : IPaymentGatewayFactory 
{
    public PaymentGatewayFactory(PaymentGatewayFoo foo, PaymentGatewayBoo boo) {...}

    public IPaymentGateway Create(bool isFoo) =>
        isFoo ? this.foo : this.boo;
}

优点和缺点:

  • 使用工厂的缺点是消费者需要了解两个抽象:工厂和IPaymentGateway.

2.使用适配器

public class OrderService
{
    private readonly IPayment _payment;

    public void DoOrder(bool isFoo)
    {
        _payment.Pay(isFoo);
    }
}

适配器可以在哪里:

public class PaymentAdapter : IPayment
{
    public PaymentAdapter(PaymentGatewayFoo foo, PaymentGatewayBoo boo) {...}

    public void Pay(bool isFoo)
    {
        var service = isFoo ? this.foo : this.boo;

        service.Pay();
    }
}

优点和缺点:

  • 这样做的好处是客户端只需要知道一个抽象。

替代实现

正如您所注意到的,在我的工厂和适配器中,实现是直接注入的。甚至不是通过它们的抽象,而是通过它们的具体类型。这可能看起来很奇怪,但只要适配器和工厂是应用程序入口点的一部分(也就是Composition Root),这样做就完全没问题。

但是可以使用其他更动态的选项,例如:

  • 注入Func<PaymentType, IPaymentGateway>委托以解析类型。
  • 注入一个Dictionary<PaymentType, IPaymentGateway>.
  • 注入一组 IPaymentGateway 实现。
  • 注入容器本身
  • 正如 Armand 建议的那样,使用动态过滤,但请注意,这会导致您将Identifier属性添加到界面中,它仅出于技术原因而存在。没有消费者(除了适配器或工厂)对此标识符感兴趣。因此,它不属于接口。更好的解决方案是在组合根中解决此问题,例如,可能通过使用属性标记实现。

推荐阅读