我正在使用MediatR来执行我的请求。在具体处理程序中,我的要求是根据具体情况在一个或多个对象实例上执行命令。
让我们举一个简单的例子。假设请求是为移动服务客户执行充值。要求规定,如果客户激活了多个服务,则必须在每个服务上完成充值。
当处理程序运行时,首先加载客户服务
var services = context.Services.Where(c => c.CustomerId == customerId);
foreach ( var service in services ) {
//Do the topup
}
为了处理充值,我使用Command模式实现了它。所以我有以下模型
其中Invoker正是MediatR处理程序实例。这是我的接收器界面
internal interface IReceiver<TRequest, TResponse>
where TRequest : DefaultRequest
where TResponse : DefaultResponse
{
TResponse Apply( TRequest request );
bool SupportsCommand( Type commandType );
}
并且有一些基于网络元素品牌(诺基亚,爱立信等)的不同实现
internal abstract class BaseNokiaReceiver<TRequest, TResponse>
: IReceiver<TRequest, TResponse>
where TRequest : DefaultRequest
where TResponse : DefaultResponse
{
public BaseNokiaReceiver( ILogger logger, DataContext context, Service service )
: base( logger, context, service ) {
}
public abstract TResponse Apply( TRequest request );
}
和具体的
internal class NokiaDataTrafficReceiver : BaseNokiaReceiver<TopupRequest, TopupResponse>
{
public NokiaDataTrafficReceiver(ILogger logger, DataContext context, Service service)
: base(logger, context, service) {
}
public override TopupResponse Apply( TopupRequest request ) {
//[...] Application code
}
}
由于客户可以在其帐户上启用多个服务,因此我必须拥有多个具有单个命令类的接收器,例如
internal abstract class AbstractCommand<TRequest, TResponse>
: ICommand<TRequest, TResponse>
where TRequest : DefaultRequest
where TResponse : DefaultResponse
{
protected IReceiver<TRequest, TResponse> _receiver;
public AbstractCommand( IReceiver<TRequest, TResponse> receiver ) {
_receiver = receiver;
}
public abstract Task<TResponse> Apply( TRequest request );
}
和
internal class Reload : AbstractCommand<TopupRequest, TopupResponse>
{
public Reload( IReceiver<TopupRequest, TopupResponse> receiver ) : base( receiver ) {
}
public async override Task<TopupResponse> Apply( TopupRequest request ) {
var response = _receiver.Apply( request );
return await Task.FromResult<TopupResponse>( response );
}
}
然后,处理程序实现变得类似于以下
var services = context.Services.Where( c => c.CustomerId = customerId );
foreach ( var service in services ) {
IReceiver<TopupRequest, TopupResponse> receiver = null;
if ( service is VoiceService ) {
receiver = new VoiceAccountReceiver();
}
else if ( service is DataService ) {
receiver = new DataTrafficReceiver();
}
Reload command = new Reload( receiver );
var result = command.Apply( input );
}
每个接收器都有一个特定的充值。
实际上接收器的实例化发生在代码中,我想以我可以使用DI容器的方式进行更改。
通过伪代码交谈,我想注册一个严格依赖于类型的Receiver实例,例如
container.Register<IReceiver<TopupRequest, TopupResponse>, VoiceAccountReceiver>()
.WhenParameterofType<VoiceService>();
container.Register<IReceiver<TopupRequest, TopupResponse>, DataTrafficReceiver>()
.WhenParameterofType<DataService>();
然后使用
在运行时解析类型container.GetInstance<IReceiver<TopupRequest, TopupResponse>>( typeof(service) );
答案 0 :(得分:2)
正如我所看到的,您缺少一些类型信息,可以让您在编译时区分操作。这似乎很明显,因为您要在VoiceService
和DataService
上添加类型检查。
所以您可以尝试将该类型信息添加到IReceiver<TRequest, TResponse>
抽象中。例如:
interface IReceiver<TRequest, TResponse, TService> { }
这样,VoiceAccountReceiver
可以按如下方式实现:
class VoiceAccountReceiver : IReceiver<TopupRequest, TopupResponse, VoiceService>
这允许根据其可用类型信息解析正确的接收器。优选地,您的处理程序应该依赖于介体抽象的类型。中介实现将负责回调容器以解析正确的类型。
例如:
var services = context.Services.Where(c => c.CustomerId == customerId);
foreach ( var service in services ) {
var result = this.receiverMediator.Apply(input, service);
}
您的IReceiverMediator
可以定义如下:
interface IReceiverMediator
{
TResponse Apply<TRequest, TService>(TRequest input, object service)
where TRequest : IRequest<TResponse>;
}
Simple Injector特定实现可以定义如下:
class SimpleInjectorReceiverMediator : IReceiverMediator
{
private readonly Container container;
public SimpleInjectorReceiverMediator(Container container) {
this.container = container;
}
public TResponse Apply<TRequest, TService>(TRequest input, object service)
where TRequest : IRequest<TResponse>
{
Type receiverType =
typeof(IReceiver<,,>).MakeGenericType(typeof(TRequest), typeof(TResponse), service.GetType());
dynamic receiver = container.GetInstance(receiverType);
return (TResponse)receiver.Action();
}
}
这样就没有条件注册了。您可以按如下方式批量注册所有接收器:
container.Register(typeof(IReceiver<,,>), assemblies);
container.Register<IReceiverMediator>(new SimpleInjectorReceiverMediator(container));