zoukankan      html  css  js  c++  java
  • .NET Core开发实战(第34课:MediatR:轻松实现命令查询职责分离模式(CQRS))--学习笔记(上)

    34 | MediatR:轻松实现命令查询职责分离模式(CQRS)

    核心对象

    IMeditator

    IRequese、IRequest

    IRequestHandler<in TRequest, TResponse>

    源码链接:
    https://github.com/witskeeper/geektime/tree/master/samples/MediatorDemo

    首先我们安装了 MediatR 的 8.0 的组件包,还安装了依赖注入框架的扩展包,以及依赖注入框架的核心组件包

    • MediatR
    • MediatR.Extensions.Microsoft.DependencyInjection
    • Microsoft.Extensions.DependencyInjection

    大家可以观察到 MediatR 的包名和命名空间少了一个 o,猜测是作者故意这样设计的,因为它具体实现里面会有一个接口和类是 Mediator,如果设置同名的话会有一些引用上的问题

    var services = new ServiceCollection();
    
    services.AddMediatR(typeof(Program).Assembly);
    

    我们在这里构建一个 ServiceCollection,然后通过一行代码将我们当前的程序集注入进去,它就可以扫描我们当前程序集相关的类,下面看一下我们定义的两个类

    internal class MyCommand : IRequest<long>
    { 
        public string CommandName { get; set; }
    }
    
    internal class MyCommandHandler : IRequestHandler<MyCommand, long>
    {
        public Task<long> Handle(MyCommand request, CancellationToken cancellationToken)
        {
            Console.WriteLine($"MyCommandHandler执行命令:{request.CommandName}");
            return Task.FromResult(10L);
        }
    }
    

    第一个类是 MyCommand,它实现了 IRequest 接口,这个接口就代表中介者要执行的命令

    第二个类是 MyCommandHandler,它实现了 IRequestHandler 的接口,这个就是我们对命令的处理器的定义

    var serviceProvider = services.BuildServiceProvider();
    
    var mediator = serviceProvider.GetService<IMediator>();
    
    await mediator.Send(new MyCommand { CommandName = "cmd01" });
    

    我们从容器里面获取一个 IMediator,然后通过 send 方法发送一个 MyCommand 命令,我们构造了一个新的 MyCommand 的实例传给它

    启动程序,输出如下:

    MyCommandHandler执行命令:cmd01
    

    我们可以看到 MyCommandHandler 的 Handle 方法执行了,它输出了 MyCommandHandler 的执行命令 cmd01

    这样子,这个中介者它有什么好处呢?

    大家可以看到,通过中介者模式,我们将命令的构造和命令的处理可以分离开,那么命令的处理如何知道要处理哪个命令呢,就是通过我们泛型的约束来定义的,我们这里为 IRequestHandler 填入了 MyCommand 类型,所以我们能明确知道 MyCommandHandler 是用来处理 MyCommand 的

    如果说我在程序里面实现了多个 Handler,我们可以试验一下

    internal class MyCommandHandlerV2 : IRequestHandler<MyCommand, long>
    {
        public Task<long> Handle(MyCommand request, CancellationToken cancellationToken)
        {
            Console.WriteLine($"MyCommandHandlerV2执行命令:{request.CommandName}");
            return Task.FromResult(10L);
        }
    }
    
    internal class MyCommandHandler : IRequestHandler<MyCommand, long>
    {
        public Task<long> Handle(MyCommand request, CancellationToken cancellationToken)
        {
            Console.WriteLine($"MyCommandHandler执行命令:{request.CommandName}");
            return Task.FromResult(10L);
        }
    }
    

    启动程序,输出如下:

    MyCommandHandlerV2执行命令:cmd01
    

    大家可以看到我们输出的是 V2 执行命令

    我们把代码进行一个调整,把这个定义移到后面

    internal class MyCommandHandler : IRequestHandler<MyCommand, long>
    {
        public Task<long> Handle(MyCommand request, CancellationToken cancellationToken)
        {
            Console.WriteLine($"MyCommandHandler执行命令:{request.CommandName}");
            return Task.FromResult(10L);
        }
    }
    
    internal class MyCommandHandlerV2 : IRequestHandler<MyCommand, long>
    {
        public Task<long> Handle(MyCommand request, CancellationToken cancellationToken)
        {
            Console.WriteLine($"MyCommandHandlerV2执行命令:{request.CommandName}");
            return Task.FromResult(10L);
        }
    }
    

    启动程序,输出如下:

    MyCommandHandler执行命令:cmd01
    

    大家可以看到我们这次输出的并不是 V2,而是之前的那个命令,为什么会这样子呢?是因为实际上 mediator 对于 IRequestHandler 的扫描,它是有顺序的,后面扫描到的会替换前面扫描到的 Handler,它只会识别其中最后注册进去的一个,也就是说我们在处理 RequestHandler 的时候,我们要注意在注册时仅注册需要的那个

    我们再来看看我们的应用程序,回到我们之前的工程里

    namespace GeekTime.API.Application.Commands
    {
        public class CreateOrderCommandHandler : IRequestHandler<CreateOrderCommand, long>
        {
            IOrderRepository _orderRepository;
            ICapPublisher _capPublisher;
            public CreateOrderCommandHandler(IOrderRepository orderRepository, ICapPublisher capPublisher)
            {
                _orderRepository = orderRepository;
                _capPublisher = capPublisher;
            }
    
    
            public async Task<long> Handle(CreateOrderCommand request, CancellationToken cancellationToken)
            {
    
                var address = new Address("wen san lu", "hangzhou", "310000");
                var order = new Order("xiaohong1999", "xiaohong", 25, address);
    
                _orderRepository.Add(order);
                await _orderRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);
                return order.Id;
            }
        }
    }
    

    我们可以看到我们的 CreateOrderCommandHandler 实现的是 IRequestHandler,这也就是解释了为什么之前我们并没有显示的调用 CreateOrderCommandHandler,代码却能够执行到这里的原因

    知识共享许可协议

    本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

    欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

    如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。

  • 相关阅读:
    POJ 1659 Frogs' Neighborhood
    zoj 2913 Bus Pass(BFS)
    ZOJ 1008 Gnome Tetravex(DFS)
    POJ 1562 Oil Deposits (DFS)
    zoj 2165 Red and Black (DFs)poj 1979
    hdu 3954 Level up
    sgu 249 Matrix
    hdu 4417 Super Mario
    SPOJ (BNUOJ) LCM Sum
    hdu 2665 Kth number 划分树
  • 原文地址:https://www.cnblogs.com/MingsonZheng/p/12590364.html
Copyright © 2011-2022 走看看