Mediat是github上的一个开源项目,项目地址:github.com/jbogard/Med…
聊一聊
学习一个项目,最关键的就是要搞懂几件事情:MediatR是什么、核心内容是什么、能用来做什么、使用场景是什么。带着这几个问题去看源码,只要源码看懂了,那么这个项目差不多也就掌握了。
看懂一个项目,还有一个非常核心的前提:面向对象的思想。
那些开发大牛,他们都有着极其抽象的抽象思想和面向对象的思想,他们对面向对象的分析和设计模式早已谙熟于心。所以对于每一位开发者来说,面向对象的思想和设计模式都应该熟悉,并且牢牢掌握。
中介者模式
开始分析MediatR项目之前,我们先来分享一个设计模式———中介者模式(Mediator)。眼神好的童子们可能会发现MediatR和Mediator两个单词非常的相似。其实并没有MediatR这个单词的。但是MediatR项目确实有中介者模式的思想。所以下面我们先来看看什么是中介者模式。
先上UML图,这是《大话设计模式》一书中的插图。(本人拜读了好多遍,本人的OOP思想也是从本书受到启发的 :laughing: )
总的来说,中介者模式中有几种角色:
- Mediator(抽象中介者):通过定义一个接口与各个同事对象进行通信。
- ConcreteMediator(具体中介者):是抽象中介者的具体实现,需要知道所有具体同事类。
- Colleague(抽象同事类):定义了通过中介者与其他同事进行通信的接口
- ConcreteColleague(具体同事类):抽象同事接口的实现类,当一个同事对象需要与其他同事机型通信时,将统一通过具体中介者来进行通信。
这几种角色也不是绝对都需要的。比如,Mediator和Colleague,可以视具体情况省略从而简化。
中介者模式概念:用一个中介对象(ConcreteMediator)来封装一系列的对象(ConcreteColleague1、ConcreteColleague2等)交互。中介者(ConcreteMediator)使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间地交互
好了,中介者模式就介绍这么多了。它的具体案例自己可以参考网页上或者书本中的,本人不再提供。
接下来,我们的硬菜开始上场。。。
MediatR
MediatR是什么
MediatR是一款基于中介者模式的思想而实现的.NET库,主要是为了解决进程内(in-process,进程内指的是同一个进程,而不是多个进程之间的)消息发送与消息处理过程之间的耦合问题。例如,一个Controller需要调用相应的Repository进行业务逻辑处理,普通情况下,可以直接在Controller注入Repository,而在使用MediatR的情况下,可以先注入MediatR,再通过MediatR发送给Repository,减少了耦合。它可跨平台,支持.NET 4.6.1+以及netstandard2.0。
MediatR核心内容是什么
MediatR源码的核心是IMediator接口,对应中介者模式中的Mediator(抽象中介者)。
这个接口定义了四个方法:
/// <summary>
/// Defines a mediator to encapsulate request/response and publishing interaction patterns
/// (抽象中介者。定义一个中介器来封装请求/响应和发布交互模式)
/// </summary>
public interface IMediator
{
/// <summary>
/// Asynchronously send a request to a single handler
/// (异步发送一个请求,到一个单一的处理程序)
/// </summary>
/// <typeparam name="TResponse">Response type</typeparam>
/// <param name="request">Request object</param>
/// <param name="cancellationToken">Optional cancellation token</param>
/// <returns>A task that represents the send operation. The task result contains the handler response</returns>
Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously send an object request to a single handler via dynamic dispatch
/// (通过动态调度将对象请求异步发送到单个处理程序)
/// </summary>
/// <param name="request">Request object</param>
/// <param name="cancellationToken">Optional cancellation token</param>
/// <returns>A task that represents the send operation. The task result contains the type erased handler response</returns>
Task<object?> Send(object request, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously send a notification to multiple handlers
/// (异步发布一个通知,到多个处理程序)
/// </summary>
/// <param name="notification">Notification object</param>
/// <param name="cancellationToken">Optional cancellation token</param>
/// <returns>A task that represents the publish operation.</returns>
Task Publish(object notification, CancellationToken cancellationToken = default);
/// <summary>
/// Asynchronously send a notification to multiple handlers
/// (异步发布一个通知,到多个处理程序)
/// </summary>
/// <param name="notification">Notification object</param>
/// <param name="cancellationToken">Optional cancellation token</param>
/// <returns>A task that represents the publish operation.</returns>
Task Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default)
where TNotification : INotification;
}
MediatR的作者也帮助我们实现了这个接口,实现类是Mediator。对应中介者模式中的ConcreteMediator(具体中介者)。当然我们自己也可以去实现IMediator接口,实现属于我们自己的具体中介者。这就是面向接口开发的好处,也映衬了OOD中的依赖倒置原则。 拜读一下源码:
/// <summary>
/// Default mediator implementation relying on single- and multi instance delegates for resolving handlers.
/// (具体中介者。实现抽象接口,它需要知道所有具体的同事类,并从具体同事类接收消息,向具体同事类发出命令)
/// </summary>
public class Mediator : IMediator
{
private readonly ServiceFactory _serviceFactory;
private static readonly ConcurrentDictionary<Type, object> _requestHandlers = new ConcurrentDictionary<Type, object>();
private static readonly ConcurrentDictionary<Type, NotificationHandlerWrapper> _notificationHandlers = new ConcurrentDictionary<Type, NotificationHandlerWrapper>();
/// <summary>
/// Initializes a new instance of the <see cref="Mediator"/> class.
/// (初始化一个新的中介者类对象实例)
/// </summary>
/// <param name="serviceFactory">The single instance factory.</param>
public Mediator(ServiceFactory serviceFactory)
{
_serviceFactory = serviceFactory;
}
/// <summary>
///
/// </summary>
/// <typeparam name="TResponse">响应类型</typeparam>
/// <param name="request">具体同事类</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
var requestType = request.GetType();//具体同事类的类型
Func<Type, object> valueFactory = t => Activator.CreateInstance(typeof(RequestHandlerWrapperImpl<,>).MakeGenericType(requestType, typeof(TResponse)));
var handler = (RequestHandlerWrapper<TResponse>) _requestHandlers.GetOrAdd(requestType, valueFactory);
return handler.Handle(request, cancellationToken, _serviceFactory);
}
/// <summary>
///
/// </summary>
/// <param name="request">具体同事类</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task<object?> Send(object request, CancellationToken cancellationToken = default)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
var requestType = request.GetType();//获取具体同事类的类型
var requestInterfaceType = requestType
.GetInterfaces()//获取具体同事类继承的接口
.FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IRequest<>));//从具体同事类继承的接口中,筛选泛型类型、且泛型类型是IRequest<>
var isValidRequest = requestInterfaceType != null;//true
if (!isValidRequest)//判断具体同事类类型是否存在
{
throw new ArgumentException($"{nameof(request)} does not implement ${nameof(IRequest)}");
}
var responseType = requestInterfaceType!.GetGenericArguments()[0];//从具体同事类request的泛型参数中,获取响应类型
var handler = _requestHandlers.GetOrAdd(requestType,
t => Activator.CreateInstance(typeof(RequestHandlerWrapperImpl<,>).MakeGenericType(requestType, responseType)));
// call via dynamic dispatch to avoid calling through reflection for performance reasons
return ((RequestHandlerBase) handler).Handle(request, cancellationToken, _serviceFactory);
}
/// <summary>
///
/// </summary>
/// <typeparam name="TNotification"></typeparam>
/// <param name="notification"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default)
where TNotification : INotification
{
if (notification == null)
{
throw new ArgumentNullException(nameof(notification));
}
return PublishNotification(notification, cancellationToken);
}
/// <summary>
///
/// </summary>
/// <param name="notification"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task Publish(object notification, CancellationToken cancellationToken = default)
{
if (notification == null)
{
throw new ArgumentNullException(nameof(notification));
}
if (notification is INotification instance)
{
return PublishNotification(instance, cancellationToken);
}
throw new ArgumentException($"{nameof(notification)} does not implement ${nameof(INotification)}");
}
/// <summary>
/// Override in a derived class to control how the tasks are awaited. By default the implementation is a foreach and await of each handler
/// </summary>
/// <param name="allHandlers">Enumerable of tasks representing invoking each notification handler</param>
/// <param name="notification">The notification being published</param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns>A task representing invoking all handlers</returns>
protected virtual async Task PublishCore(IEnumerable<Func<INotification, CancellationToken, Task>> allHandlers, INotification notification, CancellationToken cancellationToken)
{
foreach (var handler in allHandlers)
{
await handler(notification, cancellationToken).ConfigureAwait(false);
}
}
/// <summary>
///
/// </summary>
/// <param name="notification"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private Task PublishNotification(INotification notification, CancellationToken cancellationToken = default)
{
var notificationType = notification.GetType();
var handler = _notificationHandlers.GetOrAdd(notificationType,
t => (NotificationHandlerWrapper)Activator.CreateInstance(typeof(NotificationHandlerWrapperImpl<>).MakeGenericType(notificationType)));
return handler.Handle(notification, cancellationToken, _serviceFactory, PublishCore);
}
}
这里面有几个知识点需要学习:
- 作者使用了ConcurrentDictionary<TKey, TValue> 类型来承载处理程序类型。这是一个线程安全的集合类型(专业一点的话:多线程同步字典集合),其命名空间是System,Collections.Concurrent,是在.net4.0以后推出的,它的非安全类型Dictionary<TKey, TValue> 想必很多人都不陌生。
- 源码中的另一个私有字段:ServiceFactory _serviceFactory 。 可以看出MediatR的构造函数是聚合了ServiceFactory这个类型的。注释翻译:用于解析所有服务的工厂方法。对于多个实例,它将解析为IEnumerable<T>。这是作者在IOC(控制反转)以及DI(依赖注入)方面的一些实现。它是一个工厂,通过IOC和DI,当你使用的时候,指定一个类型,他将在服务容器里面直接给你解析出一个或者多个实例出来。
- MediatR支持发送两种消息。
- request/response 式消息,分派到一个处理程序,由一个处理程序处理。
- Notification messages通知式消息,分派到多个处理程序,由多个处理程序处理。
- 继续深挖源码,作者定义了两个接口类型,分别两个类文件:IRequest 和 INotification 。这两个接口没有任何的成员,在其他类型实现这两个接口的时候,它们只是起到约束的作用。因为作者在源码中大量使用了泛型类型以及泛型方法,熟悉泛型的程序猿都知道:泛型约束这么个概念。
在我们使用这个类库的时候,当我们定义的类型实现了IRequest接口的时候,那就是第一种处理消息的方式(request/response),它还分带响应和不带响应两种;当我们定义的类型实现了INotification接口的时候,那就是第二种处理消息的方式(notification messages)。
/// <summary>
/// Marker interface to represent a request with a void response
/// 标记接口,代表一个无需返回响应的请求
/// </summary>
public interface IRequest : IRequest<Unit> { }
/// <summary>
/// Marker interface to represent a request with a response
/// 标记接口,代表一个有响应类型的请求
/// </summary>
/// <typeparam name="TResponse">Response type</typeparam>
public interface IRequest<out TResponse> : IBaseRequest { }
/// <summary>
/// Allows for generic type constraints of objects implementing IRequest or IRequest{TResponse}
/// </summary>
public interface IBaseRequest { }
/// <summary>
/// Marker interface to represent a notification
/// (标记接口,代表一个通知)
/// </summary>
public interface INotification { }
- 继续深挖Mediator这个类型。你会发现在Send() 和 Publish() 方法中分别有一个RequestHandlerWrapperImpl<,> 和 NotificationHandlerWrapperImpl<>,还有RequestHandlerWrapper<TResponse> 和NotificationHandlerWrapper。这些是作者在项目中定义的一些内部类型,里面有一些C#的高级语法,比如:局部函数、表达式形式的成员函数、Linq高阶函数Aggregate、构造委托函数链,不了解的可以去了解了解。话说作者源码写得很精简,但不宜让人理解,所以有些地方我给重构了一下,方便自己能看懂。
internal abstract class RequestHandlerBase
{
/// <summary>
///
/// </summary>
/// <param name="request">具体同事类</param>
/// <param name="cancellationToken"></param>
/// <param name="serviceFactory">服务工厂</param>
/// <returns></returns>
public abstract Task<object?> Handle(object request, CancellationToken cancellationToken, ServiceFactory serviceFactory);
/// <summary>
/// 获取处理程序
/// </summary>
/// <typeparam name="THandler"></typeparam>
/// <param name="factory">服务工厂</param>
/// <returns></returns>
protected static THandler GetHandler<THandler>(ServiceFactory factory)
{
THandler handler;
try
{
handler = factory.GetInstance<THandler>();
}
catch (Exception e)
{
throw new InvalidOperationException($"Error constructing handler for request of type {typeof(THandler)}. Register your handlers with the container. See the samples in GitHub for examples.", e);
}
if (handler == null)
{
throw new InvalidOperationException($"Handler was not found for request of type {typeof(THandler)}. Register your handlers with the container. See the samples in GitHub for examples.");
}
return handler;
}
}
internal abstract class RequestHandlerWrapper<TResponse> : RequestHandlerBase
{
/// <summary>
///
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <param name="serviceFactory"></param>
/// <returns></returns>
public abstract Task<TResponse> Handle(IRequest<TResponse> request, CancellationToken cancellationToken, ServiceFactory serviceFactory);
}
/// <summary>
/// 请求处理器程序包装器的实现。
/// </summary>
/// <typeparam name="TRequest"></typeparam>
/// <typeparam name="TResponse"></typeparam>
internal class RequestHandlerWrapperImpl<TRequest, TResponse> : RequestHandlerWrapper<TResponse> where TRequest : IRequest<TResponse>
{
/// <summary>
/// 重写抽象类的Handle方法
/// </summary>
/// <param name="request">具体同事类</param>
/// <param name="cancellationToken"></param>
/// <param name="serviceFactory">服务工厂</param>
/// <returns></returns>
public override Task<object?> Handle(object request, CancellationToken cancellationToken, ServiceFactory serviceFactory)
{
return Handle((IRequest<TResponse>)request, cancellationToken, serviceFactory)
.ContinueWith(t =>
{
if (t.IsFaulted)
{
ExceptionDispatchInfo.Capture(t.Exception.InnerException).Throw();
}
return (object?)t.Result;
}, cancellationToken);
}
/// <summary>
/// 重写抽象类的Handle方法
/// </summary>
/// <param name="request">具体同事类</param>
/// <param name="cancellationToken"></param>
/// <param name="serviceFactory">服务工厂</param>
/// <returns></returns>
public override Task<TResponse> Handle(IRequest<TResponse> request, CancellationToken cancellationToken, ServiceFactory serviceFactory)
{
//此处是局部函数&&表达式形式的成员函数(C#7.0新语法)
//这一步的目的,是将一个类型函数对象转换为局部函数,方便后续调用。
//相当于下面的写法
//Task<TResponse> Handler() => GetHandler<IRequestHandler<TRequest, TResponse>>(serviceFactory).Handle((TRequest) request, cancellationToken);
Task<TResponse> Handler()
{
return GetHandler<IRequestHandler<TRequest, TResponse>>(serviceFactory)//从IOC容器获取该请求对应的请求处理器
.Handle((TRequest) request, cancellationToken);//调用Handle方法
}
//将函数调用转换为委托
RequestHandlerDelegate<TResponse> handler = () => GetHandler<IRequestHandler<TRequest, TResponse>>(serviceFactory).Handle((TRequest) request, cancellationToken);
var instances = serviceFactory.GetInstances<IPipelineBehavior<TRequest, TResponse>>();//从IOC容器获取所有的IPipelineBehavior的实现,用于构造请求管道
var func = instances.Reverse()//集合反转
.Aggregate((RequestHandlerDelegate<TResponse>) Handler,//将局部函数强转为委托,等价于handler
(next, pipeline) => () => pipeline.Handle((TRequest) request, cancellationToken, next)//借助委托构造函数链,等价于combineFunc
);
Func<RequestHandlerDelegate<TResponse>, IPipelineBehavior<TRequest, TResponse>, RequestHandlerDelegate<TResponse>> combineFunc = (delegater, behavior) =>
{
//将类型函数对象转换为委托
RequestHandlerDelegate<TResponse> combineDelegate = () => behavior.Handle((TRequest) request, cancellationToken, delegater);
//上面的写法相当于这个写法
//RequestHandlerDelegate<TResponse> combineDelegate = delegate { return behavior.Handle((TRequest)request, cancellationToken, delegater};
return combineDelegate;
};
return func();//进行委托调用并返回
}
}
6.最后一点东西:处理管道Pipeline。这里只讲一下原理和概念,不贴源码了。其实就像http请求管道,我们使用.net core在Startup类的Configure()方法中添加的中间件一样,是有一定逻辑顺序在里面的。它这个也是有顺序的,就是请求处理之前做什么、请求处理之后做什么。其实这也是用到了一个设计模式的思想。源码里有这么几个接口:IPipelineBehavior、IRequestPreProcessor、IRequestPostProcessor、IRequestExceptionAction、IRequestExceptionHandler。
MeidatR支持按需配置请求管道进行消息处理。即支持在请求处理前和请求处理后添加额外行为。仅需实现IRequestPreProcessor、IRequestPostProcessor两个接口,并将前三个服务接口注册到Ioc容器即可。
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(GenericPipelineBehavior<,>));//添加通用管道行为服务
services.AddScoped(typeof(IRequestPreProcessor<>), typeof(GenericRequestPreProcessor<>));//添加通用请求前预处理程序服务
services.AddScoped(typeof(IRequestPostProcessor<,>), typeof(GenericRequestPostProcessor<,>));//添加通用请求后处理程序服务
总体上就以上这些知识点了,基本上快将源码囊括完了,只剩下个别类型没有说明,这些对于使用不是很重用吧。。。 我们的作者提供了一个专为方便.net core依赖注入的类库项目,里面源码不多,会用即可。最后附上地址:MediatR.Extensions.Microsoft.DependencyInjection
MediatR能做什么,以及使用场景
进程内消息的发送和处理过程之间解耦。 根据中介者模式的思想,去理解这个项目。 这应该就是它的使用场景和作用。
喝个汤
最近辞了工作,做起全职爸爸了,娃娃现在10个多月了。之前是我妈在身边带孩子,现在我妈回老家了(在老家待到国庆节以后了),正好我前面工作干的也不爽,索性辞职当起全职爸爸了,然后老婆一个人上班。
说实话,带娃挺累的。他需要有人一直陪着,不然他爬来爬去,双手到处乱抓,很危险和不卫生。一会儿大人没在身边就哇哇地哭闹。。。只能等他玩累了睡着或者晚上的时候抽空学习学习。
最后附上俺娃,近期的照片一张。