中介者模式是一种常见的设计模式,旨再降低程序的耦合性,因为传统的三层模式层层之间需要显示的调用,必须上层依赖下层,耦合性很高,为了解耦,将所有的指令单独放在一个位置处理,其他位置均通过这个位置来间接的调用,从而减少耦合,具体的可以参考中介者模式,建议先了解下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

一起来学习.net core程序使用中介者模式:MediatR插件的更多相关文章

  1. MediatR一个.net中简单好用的中介者模式实现方案

    MediatRGit地址:https://github.com/jbogard/MediatR 1.安装妞盖特包 一般来说只需要安装一个MediatR就行了,.net core程序需要再安装一个Med ...

  2. .NET CORE学习笔记系列(4)——ASP.NET CORE 程序启用SSL

    一.什么是SSL? 1.概念: SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数 ...

  3. 在 ASP.NET Core 项目中使用 MediatR 实现中介者模式

    一.前言  最近有在看 DDD 的相关资料以及微软的 eShopOnContainers 这个项目中基于 DDD 的架构设计,在 Ordering 这个示例服务中,可以看到各层之间的代码调用与我们之前 ...

  4. 将Windows系统编译的.NET Core程序发布到Ubuntu系统

    在可移植方面.NET Core应用程序分为两种,Portable application(便捷,需要目标机器安装.NET Core Runtime)和Self-contained applicatio ...

  5. 从头认识一下docker-附带asp.net core程序的docker化部署

    从头认识一下docker-附带asp.net core程序的docker化部署 简介 在计算机技术日新月异的今天, Docker 在国内发展的如火如荼,特别是在一线互联网公司, Docker 的使用是 ...

  6. 使用Dapper.Contrib 开发.net core程序,兼容多种数据库

    关于Dapper的介绍,我想很多人都对它有一定的了解,这个类似一个轻型的ORM框架是目前应用非常火的一个东西,据说各方面的性能都不错,而且可以支持多种数据库,在开始介绍这个文章之前,我花了不少功夫来学 ...

  7. [译]ASP.NET Core中使用MediatR实现命令和中介者模式

    作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9866068.html 在本文中,我将解释命令模式,以及如何利用基于命令模式的第三方库来实现它们,以及如何 ...

  8. [转帖]以Windows服务方式运行ASP.NET Core程序

    以Windows服务方式运行ASP.NET Core程序 原作者blog: https://www.cnblogs.com/guogangj/p/9198031.htmlaspnet的blog 需要持 ...

  9. 创建.NET Core程序的Nuget Package

    最近在看ASP.NET Core MVC的教材,几乎每章开始都要重复从Empty project开始创建一个ASP.NET Core的项目,然后手动修改project.json,增加经典三目录(Mod ...

随机推荐

  1. ORACLE监听理解

    参考官方文档Net Services Reference的7 Oracle Net Listener Parameters (listener.ora) 1 监听概念 oracle监听,是个服务器端进 ...

  2. Centos7配置ssh免密登录群发

    ssh免密登录是客户端发送自己的公钥到服务器.用公钥进行解密,自己生成的私钥进行加密. 首先在客户端查看sshd服务是否启动 [zhiwei@zhiwei1 ~]$ ps -Af|grep sshd; ...

  3. lower_case_table_names和数据库在Linux和windows平台之间的相互迁移问题

    MySQL关于 lower_case_table_names 的文档 https://dev.mysql.com/doc/refman/5.7/en/identifier-case-sensitivi ...

  4. Python字符编码和转码

    一:Python2 python2默认编码格式是ascii码,解释器解释代码时会将代码以及代码中的字符串等转换成ascii码再执行.这样会导致字符串输出或传输时,与当前环境编码格式不同的话会显示乱码. ...

  5. 使用acme.sh申请&自动续期LetsEncrypt免费SSL证书(转)

    一.简介 LetsEncrypt是一个免费.自动.开放的证书颁发机构.acme.sh 实现了 acme 协议, 可以从 LetsEncrypt 生成免费的证书. 本文介绍如何使用acme.sh来签发并 ...

  6. linux系统盘扩容操作

    linux操作系统原来的50硬盘空间不够用了,如果再加一块60G硬盘,怎样扩容呢?今天我参考了前辈门的文档实际操作了一下,涉及到PV/VG/LV的相关操作. 当50G系统硬盘不够,再挂载一块60G,就 ...

  7. 转载--从输入URL到页面展示到底发生了什么

    最近我也在看http协议, tcp相关知识, 在吃饭时无意看到来一篇文章讲解“从输入URL到页面展示到底发生了什么”, 细细看完, 很值得回味, 所以转载, 以供日后在温习. (PS, 作者这篇文章发 ...

  8. soeasy的键盘鼠标事件

    在web自动化中,我们可能会遇到需要通过键盘或者鼠标去操作某些元素,那么我们就需要用到键盘事件和鼠标事件了,今天对键盘和鼠标操作进行一个总结 鼠标事件 鼠标事件需要引入ActionChains类,查看 ...

  9. 开始Golang之旅了

  10. luogu p2622关灯问题II

    luogu p2622关灯问题II 题目描述 现有n盏灯,以及m个按钮.每个按钮可以同时控制这n盏灯--按下了第i个按钮,对于所有的灯都有一个效果.按下i按钮对于第j盏灯,是下面3中效果之一:如果a[ ...