前言

简单整理一下Mediator。

正文

Mediator 名字是中介者的意思。

那么它和中介者模式有什么关系呢?前面整理设计模式的时候,并没有去介绍具体的中介者模式的代码实现。

如下:

https://www.cnblogs.com/aoximin/p/13600464.html

之所以没写代码就是因为它现在是一种思想,以前的简单的已经很难满足我们现在开发的需求了。

那么看下Mediator 是如何做的吧。

Mediator 这个服务,是如何让我们的命令查询职责分离的。

首先安装一下包:

Mediator 核心接口为:

  1. IMediator

  2. IRequest,IRequest

  3. IRequestHandler<in IRequest,TResponse>

一般从接口就能看到其设计思想,其余的实现,各有各的想法,那么就来看下IMediator吧。

/// <summary>
/// Defines a mediator to encapsulate request/response and publishing interaction patterns
/// </summary>
public interface IMediator : ISender, IPublisher
{
}

这个接口的作用是定义了一个中介者,这个中介者用来装入请求或者响应和实现一些交互模式。

那么看来分别就是ISender和IPublisher来实现了。

看下ISender:

/// <summary>
/// Send a request through the mediator pipeline to be handled by a single handler.
/// </summary>
public interface ISender
{
/// <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);
}

这个接口用来通过由单个处理程序的中介者管道中发送请求。

IPublisher:

/// <summary>
/// Publish a notification or event through the mediator pipeline to be handled by multiple handlers.
/// </summary>
public interface IPublisher
{
/// <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;
}

这个接口用来通过多个处理程序的中介者管道中发送通知或者事件。

好了,现在我们得到的信息有"发布"、"事件"、"请求"、"管道" 这几个名词了。

那么实际上我们也能大致的猜出它是怎么实现的。

接下来查看IRequest:

/// <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 { }

这些上面的英文已经提示了,标志性作用,用来做定义的。

最后来看下IRequestHandler接口:

/// <summary>
/// Defines a handler for a request
/// </summary>
/// <typeparam name="TRequest">The type of request being handled</typeparam>
/// <typeparam name="TResponse">The type of response from the handler</typeparam>
public interface IRequestHandler<in TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
/// <summary>
/// Handles a request
/// </summary>
/// <param name="request">The request</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Response from the request</returns>
Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken);
}

现在我们在来整理一下名词:"发布"、"事件"、"请求"、"管道" 、"处理请求"

那么大概猜测大概通过接口来处理请求,然后还可以发布事件处理的。处理请求有处理请求的管道,且这个处理是单个处理程序,而处理事件是多个处理程序。

接下来操作一遍:

async static Task Main(string[] args)
{
var services = new ServiceCollection(); services.AddMediatR(typeof(Program).Assembly); var serviceProvider = services.BuildServiceProvider(); var mediator = serviceProvider.GetService<IMediator>(); await mediator.Send(new SelfCommand{ CommandName ="zhangsan"});
} internal class SelfCommand : IRequest<long>
{
public string CommandName { get; set; }
} internal class SelfCommandHandler : IRequestHandler<SelfCommand, long>
{
public Task<long> Handle(SelfCommand request, CancellationToken cancellationToken)
{
Console.WriteLine($"处理 {nameof(SelfCommand)}请求:{request.CommandName}");
return Task.FromResult(10L);
}
}

这里就有一个疑问了,为啥SelfCommandHandler会自动称为处理程序?我们并没有设置啊。

那么就来看一下send在干什么吧:

public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
} var requestType = request.GetType(); var handler = (RequestHandlerWrapper<TResponse>)_requestHandlers.GetOrAdd(requestType,
t => (RequestHandlerBase)Activator.CreateInstance(typeof(RequestHandlerWrapperImpl<,>).MakeGenericType(requestType, typeof(TResponse)))); return handler.Handle(request, cancellationToken, _serviceFactory);
}

上面可以看到生成了一个handle,然后调用了Handle 方法。

然后进RequestHandlerWrapperImpl 查看:

internal class RequestHandlerWrapperImpl<TRequest, TResponse> : RequestHandlerWrapper<TResponse>
where TRequest : IRequest<TResponse>
{
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);
} 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))();
}
}

这里埋一个坑,因为看了一下,有很多细节的地方比如说Activator.CreateInstance的机制、一些管道细节,还设计到注册IPipelineBehavior<TRequest, TResponse>的实现类的机制,整理到该系列的细节篇中,将会比较详细的介绍。

现在只需要看Handle的ContinueWith,如果失败的话,那么会返回一个异常,否则返回结果。

然后根据上面的,我们指定Request对应的RequstHandle 必须名字有如下规律:如SelfCommand,只需要SelfCommand加长一些即可,比如说SelfCommandHandler,比如说SelfCommandHandlerV2,还可以SelfCommand2都行。

但是呢,最好改好名字,后面加Handle。

同时,如果我们写两个SelfCommandHandler和SelfCommandHandlerV2,那么是否两个都会执行?不是的,只会执行一个。

internal class SelfCommandHandler2 : IRequestHandler<SelfCommand, long>
{
public Task<long> Handle(SelfCommand request, CancellationToken cancellationToken)
{
Console.WriteLine($"处理 {nameof(SelfCommand)} V2请求:{request.CommandName}");
return Task.FromResult(10L);
}
} internal class SelfCommandHandler : IRequestHandler<SelfCommand, long>
{
public Task<long> Handle(SelfCommand request, CancellationToken cancellationToken)
{
Console.WriteLine($"处理 {nameof(SelfCommand)}请求:{request.CommandName}");
return Task.FromResult(10L);
}
}

比如说这样,那么会执行。

也就是说会执行SelfCommandHandler2。

那么请求就算介绍完了,那么看下事件。

调用事件这样调用即可:

 await mediator.Publish(new SelfEvent { EventName = "SelfEvent" });

具体类:

internal class SelfEvent : INotification
{
public string EventName { get; set; }
} internal class SelfEventHandler : INotificationHandler<SelfEvent>
{
public Task Handle(SelfEvent notification, CancellationToken cancellationToken)
{
Console.WriteLine($"SelfEventHandler 执行:{notification.EventName}"); return Task.CompletedTask;
}
} internal class SelfEventHandlerV2 : INotificationHandler<SelfEvent>
{
public Task Handle(SelfEvent notification, CancellationToken cancellationToken)
{
Console.WriteLine($"SelfEventHandlerV2 执行:{notification.EventName}"); return Task.CompletedTask;
}
}

效果:

那么和requst不同的是,注册几个就会调用几个。

好了现在回到中介者模式中来。

中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。

那么Mediator 这个模块呢,帮助我们解决了request和requesthandle之间的耦合,和 Event与EventHandle 之间的耦合。

一开始我认为是命令模式,后来一想,命令模式解决“行为请求者”与“行为实现者”的耦合。

命令模式如下:

https://www.cnblogs.com/aoximin/p/13616558.html

上面只是命令模式的一种形式哈。

下一节领域事件的处理。以上只是个人整理,如有错误,望请指点。

重新整理 .net core 实践篇—————Mediator实践[二十八]的更多相关文章

  1. 重新整理 .net core 实践篇—————领域事件[二十九]

    前文 前面整理了仓储层,工作单元模式,同时简单介绍了一下mediator. 那么就mediator在看下领域事件启到了什么作用吧. 正文 这里先注册一下MediatR服务: // 注册中间者:Medi ...

  2. 重新整理 .net core 实践篇—————异常中间件[二十]

    前言 简单介绍一下异常中间件的使用. 正文 if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } 这样写入中间件哈,那么在env环 ...

  3. 重新整理 .net core 实践篇————cookie 安全问题[三十八]

    前言 简单整理一下cookie的跨站攻击,这个其实现在不常见,因为很多公司都明确声明不再用cookie存储重要信息,不过对于老站点还是有的. 正文 攻击原理: 这种攻击要达到3个条件: 用户访问了我们 ...

  4. 重新整理 .net core 实践篇——— UseEndpoints中间件[四十八]

    前言 前文已经提及到了endponint 是怎么匹配到的,也就是说在UseRouting 之后的中间件都能获取到endpoint了,如果能够匹配到的话,那么UseEndpoints又做了什么呢?它是如 ...

  5. 重新整理 .net core 实践篇—————静态中间件[二十一]

    前言 简单整理一下静态中间件. 正文 我们使用静态文件调用: app.UseStaticFiles(); 那么这个默认会将我们根目录下的wwwroot作为静态目录. 这个就比较值得注意的,可能刚开始学 ...

  6. 重新整理 .net core 实践篇————缓存相关[四十二]

    前言 简单整理一下缓存. 正文 缓存是什么? 缓存是计算结果的"临时"存储和重复使用 缓存本质是用空间换取时间 缓存的场景: 计算结果,如:反射对象缓存 请求结果,如:DNS 缓存 ...

  7. 重新整理 .net core 实践篇—————配置系统之间谍[八](文件监控)

    前言 前文提及到了当我们的配置文件修改了,那么从 configurationRoot 在此读取会读取到新的数据,本文进行扩展,并从源码方面简单介绍一下,下面内容和前面几节息息相关. 正文 先看一下,如 ...

  8. 重新整理 .net core 实践篇————重定向攻击[三十九]

    前言 简单介绍一下重定向攻击. 正文 攻击思路: 看着上面挺复杂的,其实是一些很简单的步骤. 攻击者通过某些手段,让用户打开了一个好站点,打开的这个地址里面带有重定向信息,重定向信息就是自己伪造的站点 ...

  9. 重新整理 .net core 实践篇————配置应用[一]

    前言 本来想整理到<<重新整理.net core 计1400篇>>里面去,但是后来一想,整理 .net core 实践篇 是偏于实践,故而分开. 因为是重新整理,那么就从配置开 ...

随机推荐

  1. TLS是如何保障数据传输安全(中间人攻击)

    前言 前段时间和同事讨论HTTPS的工作原理,当时对这块知识原理掌握还是靠以前看了一些博客介绍,深度不够,正好我这位同事是密码学专业毕业的,结合他密码学角度对tls加解密这阐述,让我对这块原理有了更进 ...

  2. 腾讯云原生混合云-第三方集群弹EKS应对突发流量的利器

    作者 何鹏飞,腾讯云专家产品经理,曾作为容器私有云.TKEStack的产品经理兼架构师,参与腾讯云内部业务.外部客户容器化改造方案设计,目前负责云原生混合云产品方案设计工作. 胡晓亮,腾讯云专家工程师 ...

  3. C++ primer plus读书笔记——第4章 复合类型

    第4章 复合类型 1. 如果将sizeof运算符用于数组名,得到的将是整个数组中的字节数. 2. 如果对数组的一部分进行初始化,则编译器把其他元素设置为0.因此,将数组中的所有元素初始化为0,只要显式 ...

  4. redis分布式锁-可重入锁

    redis分布式锁-可重入锁 上篇redis实现的分布式锁,有一个问题,它不可重入. 所谓不可重入锁,即若当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到被阻塞. 同一个 ...

  5. python3读取文件指定行的三种方案

    技术背景 考虑到深度学习领域中的数据规模一般都比较大,尤其是训练集,这个限制条件对应到实际编程中就意味着,我们很有可能无法将整个数据文件的内容全部都加载到内存中.那么就需要一些特殊的处理方式,比如:创 ...

  6. 对spring创建对象时为何要使用接口

    对spring创建对象时为何要使用接口,而使用接口的实现类会报错 接上一篇问题的解答:Spring AOP获取不了增强类(额外方法)和无法通过getBean()获取对象 此问题发生在动态代理时,比如对 ...

  7. [设计模式] 设计模式课程(二十)--命令模式(Command)

    概述 "行为变化"模式:组件构建过程中,组件行为的变化经常会导致组件本身剧烈的变化."行为变化"模式将组件的行为和组件本身进行解耦,从而支持组件行为的变化,实现 ...

  8. CentOS 7.6 操作系统 安装指导书 (鲲鹏920处理器) 01

    若需要手动调整预留内存大小,请参考如下配置进行调整. 以下以配置crashkernel为512M为例进行操作说明: 命令行执行命令vi /etc/default/grub,配置"crashk ...

  9. Redis SWAPDB 命令背后做了什么

    Redis SWAPDB 命令背后做了什么 目录 Redis SWAPDB 命令背后做了什么 0x00 摘要 0x01 SWAPDB 基础 1.1 命令说明 1.2 演示 0x02 预先校验 0x03 ...

  10. pika详解 (一)

    pika详解 (一) 本文链接:https://blog.csdn.net/comprel/article/details/94592316 pika pika处理消息可以简单分为以下几个步骤: 我们 ...