Add a generic handler for Send and Publish methods of the MediatR library in asp .net core

GoldenAge

I use the CQS pattern in my asp.net core project. Let's start with an example to better explain what I want to achieve. I created a command:

public class EmptyCommand : INotification{}

The command handler:

public class EmptyCommandHandler : INotificationHandler<EmptyCommand>
{
    public Task Handle(EmptyCommand notification, CancellationToken cancellationToken)
    {
        return Task.FromResult(string.Empty);
    }
}

The query:

public class EmptyQuery : IRequest<string>{}

The query handler:

public class EmptyQueryHandler : IRequestHandler<EmptyQuery, string>
{
    public Task<string> Handle(EmptyQuery notification, CancellationToken cancellationToken)
    {
        return Task.FromResult(string.Empty);
    }
}

and this is a simple example of how to run the command and query and invoke the Handle method from the EmptyCommandHandler and EmptyQueryHandler:

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();  
}

Please bear in mind that query can return other types not necessarily the string. I would like to create some kind of a bridge class e.g. MediatorBoostrapper, which allows me to run some business logic(e.g. log command/query via Logger) every time the Publish method is invoked and then invoke the public Task Handle(EmptyCommand notification,... method from the command handler. The solution must be generic, so this method would be invoked every time I run the Publish method. I also want to be able to do the same thing for the Send method.

I was thinking about the creation of the public class MediatorBoostrapper : IMediator but not sure what should be a proper implementation of the class and if my idea is good. Any ideas? Cheers

Edit

  1. I want to have an example of how to use the Behaviors to create a generic way to run some external method from the generic handler every time I Run the Send method for queries. I want to have a similar example for Publish method, which I use for sending commands.

  2. I want to have an example of how to use Polymorphic dispatch for the creation of the GenericCommandHandler and a GenericQueryHandler

I created a sample project on GitHub which can be found here You can feel free to try to extend this project with your solution.

GoldenAge

This time I want to answer the question starting from the end.

2.

TL;DR Polymorphic Dispatch cannot be used for the CQS

After some time of playing with the MediatR library, reading the comments under my Question and consultation with my friend, I found the Polymorphic Dispatch(PD) can be used to create a generic handler only in case of the Commands. The PD solution cannot be implemented for Queries. Based on the Documentation, the handlers are contravariant and not covariant. This means the PD works only in the case where the TResponse is a constant type. In case of the Queries, this is false and each Query handler can return a different result.

I also found this issue. I think it's interesting to know you can use the Polymorphic Dispatch only if your container supports it.

1. Behaviors is the one and only solution for CQS when using the MediatR. Based on the comment under my question from #Steve and comment from jbogard I've found the way how to use Behaviors and IRequestHandler for the strict Command pattern. The full comment:

Just to summarize the changes, there are 2 main flavors of requests: those that return a value, and those that do not. The ones that do not now implement IRequest<T> where T : Unit. This was to unify requests and handlers into one single type. The diverging types broke the pipeline for many containers, the unification means you can use pipelines for any kind of request.

It forced me to add the Unit type in all cases, so I've added some helper classes for you.

  • IRequestHandler<T> - implement this and you will return Task<Unit>.
  • AsyncRequestHandler<T> - inherit this and you will return Task.
  • RequestHandler<T> - inherit this and you will return nothing (void).

For requests that do return values:

  • IRequestHandler<T, U> - you will return Task<U>
  • RequestHandler<T, U> - you will return U

I got rid of the AsyncRequestHandler because it really wasn't doing anything after the consolidation, a redundant base class.

The example

a) The Commands management:

public class EmptyCommand : IRequest{...}

public class EmptyCommandHandler : RequestHandler<EmptyCommand>
{
    protected override void Handle(EmptyCommand request){...}
}

b) The Queries management:

// 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) The sample LogginBehavior class:

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) To register the LoggingBehavior add the command

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

to the body of the ConfigureServices method in the Startup.cs.

e) The example of how to run sample command and query:

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

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

the program is not able to find handler for MediatR query ASP.Net Core

MediatR publish and MediatR send

How do I register and use a MediatR pipeline handler, for ASP.NET Core?

How to create generic route handler in ASP.NET Core

MediatR with ASP.NET Core DI

How to use generic methods in Controller with ASP.NET Core

ASP.NET Core MediatR error: Register your handlers with the container

Mediatr unable to resolve UserManager in ASP.Net Core

ASP.NET Core precompiled views on publish

Which files to publish? ASP .Net Core 2.0

Publish the Views with asp.net core

ASP .Net Core 2.2 publish to Azure

Publish ASP NET Core With Clean Achitecture

ASP.NET Core Pages Handler

Asp .Net Core policy handler not triggering

Http handler in asp.net core

Can't add a simple POST handler to an ASP.NET Core MVC web app

How to add global exception handler for gRPC services in ASP.NET Core?

How to add mediatr in .NET 6?

Unable to add "WindowsAzure.Storage" dependency to .Net Core (ASP.NET 5) class library

Publish simple Asp.Net Core App (.Net Framework) with WebJob

Discovering Generic Controllers in ASP.NET Core

ASP Net Core 2.2 add locker icon only to methods that require authorization - Swagger UI

CQRS - Creating BaseCommandHandler using Mediatr in C#, ASP.net Core

Registering a MediatR pipeline constrained PostProcessor using ASP.NET Core DI

How to get the integer value returned in Task <int> format - Asp.net Core MVC - MediaTr

MediatR CQRS - How to deal with unexisting resources (asp.net core web api)

Getting Null Value When Input List Files in Asp .Net Core with Mediatr

ASP.NET Core deployment methods

TOP Ranking

  1. 1

    Failed to listen on localhost:8000 (reason: Cannot assign requested address)

  2. 2

    Loopback Error: connect ECONNREFUSED 127.0.0.1:3306 (MAMP)

  3. 3

    How to import an asset in swift using Bundle.main.path() in a react-native native module

  4. 4

    pump.io port in URL

  5. 5

    Compiler error CS0246 (type or namespace not found) on using Ninject in ASP.NET vNext

  6. 6

    BigQuery - concatenate ignoring NULL

  7. 7

    ngClass error (Can't bind ngClass since it isn't a known property of div) in Angular 11.0.3

  8. 8

    ggplotly no applicable method for 'plotly_build' applied to an object of class "NULL" if statements

  9. 9

    Spring Boot JPA PostgreSQL Web App - Internal Authentication Error

  10. 10

    How to remove the extra space from right in a webview?

  11. 11

    java.lang.NullPointerException: Cannot read the array length because "<local3>" is null

  12. 12

    Jquery different data trapped from direct mousedown event and simulation via $(this).trigger('mousedown');

  13. 13

    flutter: dropdown item programmatically unselect problem

  14. 14

    How to use merge windows unallocated space into Ubuntu using GParted?

  15. 15

    Change dd-mm-yyyy date format of dataframe date column to yyyy-mm-dd

  16. 16

    Nuget add packages gives access denied errors

  17. 17

    Svchost high CPU from Microsoft.BingWeather app errors

  18. 18

    Can't pre-populate phone number and message body in SMS link on iPhones when SMS app is not running in the background

  19. 19

    12.04.3--- Dconf Editor won't show com>canonical>unity option

  20. 20

    Any way to remove trailing whitespace *FOR EDITED* lines in Eclipse [for Java]?

  21. 21

    maven-jaxb2-plugin cannot generate classes due to two declarations cause a collision in ObjectFactory class

HotTag

Archive