一起来学习.net core程序使用中介者模式:MediatR插件
中介者模式是一种常见的设计模式,旨再降低程序的耦合性,因为传统的三层模式层层之间需要显示的调用,必须上层依赖下层,耦合性很高,为了解耦,将所有的指令单独放在一个位置处理,其他位置均通过这个位置来间接的调用,从而减少耦合,具体的可以参考中介者模式,建议先了解下DDD里面的事件总线和命令分发。
实现中介者模式有很多方式,例如MediatR就是一种很好用的插件,作者的介绍是这样说的“.NET中的简单中介实现,没有依赖关系的进程内消息传递。通过C#泛型方差支持请求/响应,命令,查询,通知和事件,同步和异步与智能调度。”。这里推荐一款插件Dnspy可以查看dll的源码,这样就可以知道我们所使用的的插件的原理了。
这里先建好一个.Net Core程序,。然后程序入口处注入MeditaR
services.AddMediatR(typeof(Startup));
//注册发布订阅中介处理
services.AddScoped<IMediatorHandler,MediatorHandler>();
services.AddScoped<IRequestHandler<CreateUserCommand, Unit>, UserCommandHandler>();
对于中介,肯定要先做一个中介处理器,因为程序是面向接口的,所以先定义一个接口,用来表示中介处理器
/// <summary> /// 中介处理接口 /// </summary> public interface IMediatorHandler { /// <summary> /// 发送领域事件 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="command"></param> /// <returns></returns> Task SendCommand<T>(T command) where T :Command; }
然后就是它的实现类
public class MediatorHandler: IMediatorHandler { private readonly IMediator _mediator; public InMemoryBus(IMediator mediator) { _mediator = mediator; } /// <summary> /// 发送邻域事件 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="command"></param> /// <returns></returns> public Task SendCommand<T>(T command) where T : Command { try { return _mediator.Send(command); } catch (AggregateException ex) { throw ex; } } }
这里我们用到了IMediator,我们可以根据Dnspy这个工具查看到IMediator 的Send的方法的实现,
// MediatR.Mediator // Token: 0x06000018 RID: 24 RVA: 0x00002100 File Offset: 0x00000300 public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default(CancellationToken)) { if (request == null) { throw new ArgumentNullException("request"); } Type requestType = request.GetType(); return ((RequestHandlerWrapper<TResponse>)Mediator._requestHandlers.GetOrAdd(requestType, (Type t) => Activator.CreateInstance(typeof(RequestHandlerWrapperImpl<, >).MakeGenericType(new Type[] { requestType, typeof(TResponse) })))).Handle(request, cancellationToken, this._serviceFactory); }
首先我们看到它的参数IRequest<TResponse> request,所以我们可以知道Task SendCommand<T>(T command),这个方法的T一定是继层自IRequest,所以需要我们写一个基类继层于IRequest
/// <summary> /// 领域命令基类 /// </summary> public class Command:IRequest { } /// <summary> /// 创建用户领域命令 /// </summary> public class CreateUserCommand: Command { public CreateUserCommand(User user) { User = user; } public User User { get; private set; } }
因为项目需要添加功能,好比注册一个用户,所以有一个添加用户的Service代码,然后在构造函数里注入IMediatorHandler
public class UserService :IUserService { private readonly IUserRepository _userRepository; private readonly IMediatorHandler _mediatorHandler; public UserService(IUserRepository userRepository, IMediatorHandler mediatorHandler) { _userRepository = userRepository; _mediatorHandler = mediatorHandler; } public void Insert(User user) { var command = new CreateUserCommand(user); try { Task task = _mediatorHandler.SendCommand(command); Task.WaitAll(task); } catch (AggregateException) { throw new FrameworkException("程序内部错误"); } } }
public class UserCommandHandler : IRequestHandler<CreateUserCommand, Unit> { private readonly IUserRepository _userRepository; private readonly IMediatorHandler _mediatorHandler; public UserCommandHandler(IUserRepository userRepository, IMediatorHandler mediatorHandler) { _userRepository = userRepository; _mediatorHandler = mediatorHandler; } public Task<Unit> Handle(CreateUserCommand command, CancellationToken cancellationToken) { _userRepository.Insert(command.User); return Task.FromResult(new Unit()); } }
到这里可以说已经完成了,可以执行了,这样的例子网上也有很多。但是还是需要去了解它的源码,但是自己目前自己只是读懂了源码的20%左右,根据我的理解就是我们在调用IMediator的Send方法时,线程安全集合_requestHandlers会把我们的请求添加到内存里面,具体的就是CreateInstance一个RequestHandlerWrapperImpl对象,但是通过MakeGenericType把它的类型变成了我们请求的类型和返回的类型,然后通过(RequestHandlerWrapper<TResponse>)将Mediator转换类型调用Handler,继续执行RequestHandlerWrapper的实现类RequestHandlerWrapperImpl,然后通过某种方式调用了IRequestHandler,然后找到IRequestHandler的实现类UserCommandHandler,从而完成添加功能。
无奈自己才疏学浅,并没有理解作者的意思。我现在还是有很多疑问。
,以下是我自认为比较重要的的位置了:
1 ,这个类我删了很多代码,只留了我们用的到的 Send方法MediatorHandler类里面用到的
public class Mediator : IMediator { // Token: 0x06000017 RID: 23 RVA: 0x000020F1 File Offset: 0x000002F1 public Mediator(ServiceFactory serviceFactory) { this._serviceFactory = serviceFactory; } // Token: 0x06000018 RID: 24 RVA: 0x00002100 File Offset: 0x00000300 public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default(CancellationToken)) { if (request == null) { throw new ArgumentNullException("request"); } Type requestType = request.GetType(); return ((RequestHandlerWrapper<TResponse>)Mediator._requestHandlers.GetOrAdd(requestType, (Type t) => Activator.CreateInstance(typeof(RequestHandlerWrapperImpl<, >).MakeGenericType(new Type[] { requestType, typeof(TResponse) })))).Handle(request, cancellationToken, this._serviceFactory); } // Token: 0x04000001 RID: 1 private readonly ServiceFactory _serviceFactory; // Token: 0x04000002 RID: 2 private static readonly ConcurrentDictionary<Type, object> _requestHandlers = new ConcurrentDictionary<Type, object>(); }
2,
public interface IPipelineBehavior<in TRequest, TResponse> { // Token: 0x0600000C RID: 12 Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next); }
3
internal abstract class RequestHandlerWrapper<TResponse> : RequestHandlerBase { public abstract Task<TResponse> Handle(IRequest<TResponse> request, CancellationToken cancellationToken, ServiceFactory serviceFactory); } internal class RequestHandlerWrapperImpl<TRequest, TResponse> : RequestHandlerWrapper<TResponse> where TRequest : IRequest<TResponse> { public override Task<TResponse> Handle(IRequest<TResponse> request, CancellationToken cancellationToken, ServiceFactory serviceFactory) { Task<TResponse> Handler() => GetHandler<IRequestHandler<TRequest, TResponse>>(serviceFactory).Handle((TRequest) request, cancellationToken); return serviceFactory .GetInstances<IPipelineBehavior<TRequest, TResponse>>() .Reverse() .Aggregate((RequestHandlerDelegate<TResponse>) Handler, (next, pipeline) => () => pipeline.Handle((TRequest)request, cancellationToken, next))(); } }
4,
public abstract class RequestHandler<TRequest> : IRequestHandler<TRequest>, IRequestHandler<TRequest, Unit> where TRequest : IRequest { // Token: 0x06000014 RID: 20 RVA: 0x000020DB File Offset: 0x000002DB Task<Unit> IRequestHandler<TRequest, Unit>.Handle(TRequest request, CancellationToken cancellationToken) { this.Handle(request); return Unit.Task; } // Token: 0x06000015 RID: 21 protected abstract void Handle(TRequest request); }
4