Sha*_*ani 10 c# dependency-injection .net-core
我有两个类PaymentGatewayFoo
,PaymentGatewayBoo
它们都实现了一个公共接口IPaymentGateway
:
interface IPaymentGateway { }
class PaymentGatewayFoo : IPaymentGateway { }
class PaymentGatewayBoo : IPaymentGateway { }
Run Code Online (Sandbox Code Playgroud)
客户端请求有一个标识符,用于确定要使用的实现:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
如何根据客户端在运行时的请求来解决正确的实现?
这个问题不是重复的,它相似但它是关于两个独立的控制器(即使答案表明代码甚至不需要条件依赖注入),在我的情况下,基于客户端属性的运行时需要条件依赖价值。
这里有几个选项,但对我来说最明显的两个是使用工厂或适配器模式。
public class OrderService
{
private readonly IPaymentGatewayFactory _factory;
public void DoOrder(bool isFoo)
{
var service = _factory.Create(isFoo);
this._service.Pay();
}
}
Run Code Online (Sandbox Code Playgroud)
工厂可以在哪里:
public class PaymentGatewayFactory : IPaymentGatewayFactory
{
public PaymentGatewayFactory(PaymentGatewayFoo foo, PaymentGatewayBoo boo) {...}
public IPaymentGateway Create(bool isFoo) =>
isFoo ? this.foo : this.boo;
}
Run Code Online (Sandbox Code Playgroud)
使用工厂的缺点是消费者需要了解两个抽象:工厂和 IPaymentGateway。
public class OrderService
{
private readonly IPayment _payment;
public void DoOrder(bool isFoo)
{
_payment.Pay(isFoo);
}
}
Run Code Online (Sandbox Code Playgroud)
适配器可以在哪里:
public class PaymentAdapter : IPayment
{
public PaymentAdapter(PaymentGatewayFoo foo, PaymentGatewayBoo boo) {...}
public void Pay(bool isFoo)
{
var service = isFoo ? this.foo : this.boo;
service.Pay();
}
}
Run Code Online (Sandbox Code Playgroud)
这样做的好处是客户端只需要知道一个抽象。缺点是您需要第二个抽象。
正如您所注意到的,在我的工厂和适配器中,实现是直接注入的。甚至不是通过它们的抽象,而是通过它们的具体类型。这可能看起来很奇怪,但只要适配器和工厂是应用程序入口点(也称为Composition Root)的一部分,这样做完全没问题。
但是可以使用其他更动态的选项,例如:
Func<PaymentType, IPaymentGateway>
委托来解析类型。Dictionary<PaymentType, IPaymentGateway>
.Identifier
属性添加到界面中,它仅出于技术原因而存在。没有消费者(适配器或工厂除外)对此标识符感兴趣。因此,它不属于接口。更好的解决方案是在 Composition Root 中解决这个问题,例如,可能通过使用属性标记实现。在尝试在这里实现很好的答案之后,看起来原生 DI .net 核心容器不支持注入可数(就像IPaymentGateway[]
@armand 建议的那样)所以我最终得到了一个基于 switch case 的委托解析器(不是在以节省性能):
启动文件
services.AddTransient<PaymentGatewayResolver>(serviceProvider => key =>
{
switch (key)
{
case E_PaymentGatewayType.Foo:
return serviceProvider.GetService<PaymentGatewayFoo>();
case E_PaymentGatewayType.Boo:
return serviceProvider.GetService<PaymentGatewayBoo>();
case E_PaymentGatewayType.Undefined:
default:
throw new NotSupportedException($"PaymentGatewayRepositoryResolver, key: {key}");
}
});
Run Code Online (Sandbox Code Playgroud)
代表:
public delegate IPaymentGateway PaymentGatewayResolver(E_PaymentGatewayType paymentGatewayType);
Run Code Online (Sandbox Code Playgroud)
客户订购服务:
private readonly PaymentGatewayResolver _paymentGatewayResolver;
public OrderService(PaymentGatewayResolver paymentGatewayResolver)
{
this._paymentGatewayResolver = paymentGatewayResolver;
}
public DoOrder(E_PaymentGatewayType paymentGatewayType)
{
IPaymentGateway paymentGateway = this._paymentGatewayResolver(paymentGatewayType);
paymentGateway.Pay();
}
Run Code Online (Sandbox Code Playgroud)