在 asp .net 核心中为 MediatR 库的发送和发布方法添加通用处理程序

Posted

技术标签:

【中文标题】在 asp .net 核心中为 MediatR 库的发送和发布方法添加通用处理程序【英文标题】:Add a generic handler for Send and Publish methods of the MediatR library in asp .net core 【发布时间】:2019-05-22 13:26:10 【问题描述】:

我在我的 asp.net 核心项目中使用 CQS 模式。让我们从一个例子开始,以更好地解释我想要实现的目标。我创建了一个命令:

public class EmptyCommand : INotification

命令处理程序:

public class EmptyCommandHandler : INotificationHandler<EmptyCommand>

    public Task Handle(EmptyCommand notification, CancellationToken cancellationToken)
    
        return Task.FromResult(string.Empty);
    

查询:

public class EmptyQuery : IRequest<string>

查询处理程序:

public class EmptyQueryHandler : IRequestHandler<EmptyQuery, string>

    public Task<string> Handle(EmptyQuery notification, CancellationToken cancellationToken)
    
        return Task.FromResult(string.Empty);
    

这是一个简单的示例,说明如何从 EmptyCommandHandler 和 EmptyQueryHandler 运行命令和查询并调用 Handle 方法:

readonly IMediator _mediator;

public HomeController(IMediator mediator)

    _mediator = mediator;


public async Task<IActionResult> Index()

    await _mediator.Publish(new EmptyCommand());
    var queryResult = await _mediator.Send(new EmptyQuery());   
    return View();  

请记住,查询可以返回其他类型,不一定是string。 我想创建某种桥梁课程,例如MediatorBoostrapper,它允许我在每次调用 Publish 方法时运行一些业务逻辑(例如,通过 Logger 记录命令/查询),然后 从命令处理程序调用public Task Handle(EmptyCommand notification,... 方法。解决方案必须是通用的,因此每次运行Publish 方法时都会调用此方法。我也希望能够为 Send 方法做同样的事情。

我正在考虑创建public class MediatorBoostrapper : IMediator 但不确定该类的正确实现应该是什么,以及我的想法是否好。 有任何想法吗?干杯

编辑

    我想举个例子说明如何使用Behaviors 每次我为查询运行Send 方法时,创建一种通用方法来从通用处理程序运行一些外部方法。我想为Publish 方法提供一个类似的示例,用于发送命令。

    我想举个例子说明如何使用Polymorphic dispatch 用于创建 GenericCommandHandler 和 GenericQueryHandler

我在 GitHub 上创建了一个示例项目,可以找到 here 您可以随意尝试使用您的解决方案扩展此项目。

【问题讨论】:

你能解释一下你在这门课上要做什么吗?记住 CQS 中的 S 代表什么:分离。不要在每个应该是单独逻辑的调用中包含逻辑。如果它足够简单,只需将其添加到您的处理程序中。或者扩展类,但这在很大程度上取决于它需要做什么。 github.com/jbogard/MediatR/wiki/Behaviors 我想创建某种中间件,这样我就可以使用记录器记录每个命令和查询 在这种情况下,我要遵守 DRY 规则。 彼得这很有趣,需要尝试这种方法,看看我能实现什么 【参考方案1】:

这次我想从头开始回答这个问题。

2.

TL;DR 多态调度不能用于 CQS

在玩了一段时间的 MediatR 库后,阅读了我的 Question 下的 cmets 并咨询了我的朋友,我发现 Polymorphic Dispatch(PD) 只能在命令的情况。 PD 解决方案无法针对 Queries 实施。基于Documentation,处理程序是逆变而不是协变的。这意味着 PDTResponse 是常量类型的情况下工作。在查询的情况下,这是错误的,每个查询处理程序都可以返回不同的结果。

我还找到了this issue。我认为只有在容器支持的情况下才能使用 Polymorphic Dispatch,这很有趣。

1. Behaviors 是使用 MediatR 时 CQS 的唯一且唯一的解决方案。 根据我在#Steve 和comment from jbogard 的问题下的评论,我找到了如何将 Behaviors 和 IRequestHandler 用于严格的命令模式的方法。完整评论:

总结一下变化,请求主要有两种风格: 那些返回值的,以及那些不返回值的。那些不 现在实现IRequest&lt;T&gt; where T : Unit。这是为了统一请求 和处理程序合并为一种类型。不同的类型打破了 许多容器的管道,统一意味着您可以使用 任何类型的请求的管道。

它迫使我在所有情况下都添加 Unit 类型,所以我为你添加了一些帮助类。

IRequestHandler&lt;T&gt; - 执行此操作,您将返回 Task&lt;Unit&gt;AsyncRequestHandler&lt;T&gt; - 继承它,你将返回 TaskRequestHandler&lt;T&gt; - 继承它,你将不会返回任何东西 (void)

对于确实返回值的请求:

IRequestHandler&lt;T, U&gt; - 你将返回Task&lt;U&gt; RequestHandler&lt;T, U&gt; - 你将返回U

我去掉了 AsyncRequestHandler,因为它在合并后真的没有做任何事情,一个冗余的基类。

示例

a) 命令管理:

public class EmptyCommand : IRequest...

public class EmptyCommandHandler : RequestHandler<EmptyCommand>

    protected override void Handle(EmptyCommand request)...

b) 查询管理:

// can be any other type not necessarily `string`
public class EmptyQuery : IRequest<string>...

public class EmptyQueryHandler : IRequestHandler<EmptyQuery, string>

    public Task<string> Handle(EmptyQuery notification, CancellationToken cancellationToken)
    
        return Task.FromResult("Sample response");
    

c) 示例LogginBehavior 类:

public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>

    readonly ILogger<LoggingBehavior<TRequest, TResponse>> _logger;

    public LoggingBehavior(ILogger<LoggingBehavior<TRequest, TResponse>> logger)
    
        _logger = logger;
    

    public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
    
        var requestType = typeof(TRequest).Name;
        var response = await next();

        if (requestType.EndsWith("Command"))
        
            _logger.LogInformation($"Command Request: request");
        
        else if (requestType.EndsWith("Query"))
        
            _logger.LogInformation($"Query Request: request");
            _logger.LogInformation($"Query Response: response");
        
        else
        
            throw new Exception("The request is not the Command or Query type");
        

        return response;
    


d) 要注册LoggingBehavior,请添加命令

services.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>));

到 Startup.cs 中 ConfigureServices 方法的主体。

e) 如何运行示例命令和查询示例:

await _mediator.Send(new EmptyCommand());
var result = await _mediator.Send(new EmptyQuery());

【讨论】:

【参考方案2】:

MediatR 支持将通知发送到通用处理程序 (polymorphic dispatch)。例如:

public class GenericHandler<TNotification> : INotificationHandler<TNotification> 
    where TNotification : INotification

    public Task Handle(TNotification notification, CancellationToken cancellationToken)
    
        return Task.CompletedTask;
    

将为通过Publish() 发布的每个通知调用此处理程序。请求(查询/命令)也是如此。你也应该看看behaviors。

如果您将 MediatR 与 ASP.NET Core 一起使用,我建议您使用 MediatR.Extensions.Microsoft.DependencyInjection 库,它负责将所有处理程序连接在一起。

【讨论】:

在我将GenericHandler 添加到我的项目并尝试运行解决方案后,我收到了错误:Cannot instantiate implementation type 'HighElo.Domain.CommandHandlers.GenericHandler 我是否需要向 Startup.cs 添加一些额外的配置? @GoldenAge 看看这个库:github.com/jbogard/…。它处理请求处理程序的注册。 我已经在使用这个库了。我尝试使用许多不同的方法注册GenericHandler,例如services.AddMediatR(typeof(GenericHandler&lt;EmptyCommand&gt;)); 仍然会抛出同样的错误。不知道我应该如何注册它.. 您只需要调用services.AddMediatR(),它就会将所有请求及其处理程序连接在一起。 所以,我创建了一个完全独立的解决方案来测试你的想法,我将它上传到 GitHub 并且它可以工作。当我将相同的代码添加到我现在正在处理的项目中时,我收到了我之前提到的这个奇怪的错误。您可以在此处找到示例:github.com/demos666/mediatr-generic-handler 在我的例子中,我将services.AddMediatR(typeof(Startup),typeof(EmptyClass)); 添加到 Startup.cs 以使其工作,因为我将命令和查询存储在单独的项目中。我还需要实现GenericQueryHandler

以上是关于在 asp .net 核心中为 MediatR 库的发送和发布方法添加通用处理程序的主要内容,如果未能解决你的问题,请参考以下文章

Mediatr 无法解析 ASP.Net Core 中的 UserManager

ASP.NET Core MediatR 错误:向容器注册处理程序

ASP.NET Core MVC 入门到精通 - 3. 使用MediatR

如何为 ASP.NET Core 注册和使用 MediatR 管道处理程序?

ASP.NET Core MediatR错误:使用容器注册处理程序

带有 ASP.NET Core DI 的 MediatR