Developing your easy alternative to the MediatR library in C#
In recent weeks, there has been a rumor that the famous MediatR library, widely used by the C#/.NET developer community, especially for projects that implement the CQRS pattern, will enter the commercial license. What I want to make clear, initially, is that one does not need the other to exist. However, when talking about a solution that implements CQRS, developers immediately think of the Mediator pattern and, of course, the library by Jimmy Bogard, who is also behind another famous library in the .NET world, AutoMapper, which, by the way, will also be commercialized. Of course, you say at first? Oh, what do you mean? Until now it was open source and he had said that he would never make it commercial. (He wrote this when asked once). But that's okay, he has the right to change his mind, especially when circumstances change. He himself explained on his blog that the main reason for charging for use is to maintain the project and the necessary updates, which was previously paid for by the company he worked for. That said, I wanted to present an easy way to make your own solution to replace MediatR. Of course, here is a much simpler solution, with fewer features, but it serves the basics of handling query and command calls. Let's get to the solution: First you will have to create 4 interfaces, 2 for commands and 2 for queries. One of them will be the contract for the handler and the other for the dispatcher. IQueryHandler: namespace MyAppCQRS.Application.Configuration; public interface IQueryHandler { Task Handle(TQuery query, CancellationToken cancellationToken); } IQueryDispatcher: namespace MyAppCQRS.Application.Configuration; public interface IQueryDispatcher { Task Dispatch(TQuery query, CancellationToken cancellationToken); } ICommandHandler: namespace MyAppCQRS.Application.Configuration; public interface ICommandHandler { Task Handle(TCommand command, CancellationToken cancellationToken); } ICommandDispatcher namespace MyAppCQRS.Application.Configuration; public interface ICommandDispatcher { Task Dispatch(TCommand command, CancellationToken cancellationToken); } For the magic to happen, we now have to implement the Dispatcher Interfaces to inject the IServiceProvider that will be responsible for retrieving the instances of IQueryHandler and ICommandHandler interfaces. These instances in turn represent the Handlers for the specific queries or commands and their result types. QueryDispatcher: using Microsoft.Extensions.DependencyInjection; namespace MyAppCQRS.Application.Configuration.Dispatchers; public class QueryDispatcher(IServiceProvider serviceProvider) : IQueryDispatcher { private readonly IServiceProvider _serviceProvider = serviceProvider; public Task Dispatch(TQuery query, CancellationToken cancellationToken) { var handler = _serviceProvider.GetRequiredService(); return handler.Handle(query, cancellationToken); } } CommandDispatcher: using Microsoft.Extensions.DependencyInjection; namespace MyAppCQRS.Application.Configuration.Dispatchers; public class CommandDispatcher(IServiceProvider serviceProvider) : ICommandDispatcher { private readonly IServiceProvider _serviceProvider = serviceProvider; public Task Dispatch(TCommand command, CancellationToken cancellationToken) { var handler = _serviceProvider.GetRequiredService(); return handler.Handle(command, cancellationToken); } } How Can I use that? Well, now you can use this implementation and have the separation between queries and commands and the decoupling so desired with the mediator pattern. As an example, I implemented a User query by ID. To do this, you must create 2 classes, one that represents the query and the other the handler. Also create the dto object that will be the return in your API controller. UserResponseDto: namespace MyAppCQRS.Application.Dtos.Response.Users; public record UserResponseDto(Guid Id, string Name); GetUserByIdQuery: namespace MyAppCQRS.Application.UseCases.Users; public class GetUserByIdQuery { public Guid UserId { get; set; } public GetUserByIdQuery(Guid userId) { UserId = userId; } } GetUserByIdQueryHandler: using MyAppCQRS.Application.Configuration; using MyAppCQRS.Application.Dtos.Response.Users; namespace MyAppCQRS.Application.UseCases.Users; public class GetUserByIdQueryHandler : IQueryHandler { public GetUserByIdQueryHandler() { } public Task Handle(GetUserByIdQuery query, CancellationToken cancellationToken) { return Task.FromResult(new UserResponseDto(query.UserId, "John Doe")); } } Now, you are ready to call this in your controller. In the controller, you need to inject the Dispatcher services and then use them in all the endpoints that are necessary. To demonstrate the use, I created a controller called UsersController with a GetBy

In recent weeks, there has been a rumor that the famous MediatR library, widely used by the C#/.NET developer community, especially for projects that implement the CQRS pattern, will enter the commercial license.
What I want to make clear, initially, is that one does not need the other to exist. However, when talking about a solution that implements CQRS, developers immediately think of the Mediator pattern and, of course, the library by Jimmy Bogard, who is also behind another famous library in the .NET world, AutoMapper, which, by the way, will also be commercialized.
Of course, you say at first? Oh, what do you mean? Until now it was open source and he had said that he would never make it commercial. (He wrote this when asked once).
But that's okay, he has the right to change his mind, especially when circumstances change. He himself explained on his blog that the main reason for charging for use is to maintain the project and the necessary updates, which was previously paid for by the company he worked for.
That said, I wanted to present an easy way to make your own solution to replace MediatR. Of course, here is a much simpler solution, with fewer features, but it serves the basics of handling query and command calls.
Let's get to the solution:
First you will have to create 4 interfaces, 2 for commands and 2 for queries. One of them will be the contract for the handler and the other for the dispatcher.
IQueryHandler:
namespace MyAppCQRS.Application.Configuration;
public interface IQueryHandler
{
Task Handle(TQuery query, CancellationToken cancellationToken);
}
IQueryDispatcher:
namespace MyAppCQRS.Application.Configuration;
public interface IQueryDispatcher
{
Task Dispatch(TQuery query, CancellationToken cancellationToken);
}
ICommandHandler:
namespace MyAppCQRS.Application.Configuration;
public interface ICommandHandler
{
Task Handle(TCommand command, CancellationToken cancellationToken);
}
ICommandDispatcher
namespace MyAppCQRS.Application.Configuration;
public interface ICommandDispatcher
{
Task Dispatch(TCommand command, CancellationToken cancellationToken);
}
For the magic to happen, we now have to implement the Dispatcher Interfaces to inject the IServiceProvider that will be responsible for retrieving the instances of IQueryHandler and ICommandHandler interfaces. These instances in turn represent the Handlers for the specific queries or commands and their result types.
QueryDispatcher:
using Microsoft.Extensions.DependencyInjection;
namespace MyAppCQRS.Application.Configuration.Dispatchers;
public class QueryDispatcher(IServiceProvider serviceProvider) : IQueryDispatcher
{
private readonly IServiceProvider _serviceProvider = serviceProvider;
public Task Dispatch(TQuery query, CancellationToken cancellationToken)
{
var handler = _serviceProvider.GetRequiredService>();
return handler.Handle(query, cancellationToken);
}
}
CommandDispatcher:
using Microsoft.Extensions.DependencyInjection;
namespace MyAppCQRS.Application.Configuration.Dispatchers;
public class CommandDispatcher(IServiceProvider serviceProvider) : ICommandDispatcher
{
private readonly IServiceProvider _serviceProvider = serviceProvider;
public Task Dispatch(TCommand command, CancellationToken cancellationToken)
{
var handler = _serviceProvider.GetRequiredService>();
return handler.Handle(command, cancellationToken);
}
}
How Can I use that?
Well, now you can use this implementation and have the separation between queries and commands and the decoupling so desired with the mediator pattern. As an example, I implemented a User query by ID. To do this, you must create 2 classes, one that represents the query and the other the handler. Also create the dto object that will be the return in your API controller.
UserResponseDto:
namespace MyAppCQRS.Application.Dtos.Response.Users;
public record UserResponseDto(Guid Id, string Name);
GetUserByIdQuery:
namespace MyAppCQRS.Application.UseCases.Users;
public class GetUserByIdQuery
{
public Guid UserId { get; set; }
public GetUserByIdQuery(Guid userId)
{
UserId = userId;
}
}
GetUserByIdQueryHandler:
using MyAppCQRS.Application.Configuration;
using MyAppCQRS.Application.Dtos.Response.Users;
namespace MyAppCQRS.Application.UseCases.Users;
public class GetUserByIdQueryHandler : IQueryHandler
{
public GetUserByIdQueryHandler() { }
public Task Handle(GetUserByIdQuery query, CancellationToken cancellationToken)
{
return Task.FromResult(new UserResponseDto(query.UserId, "John Doe"));
}
}
Now, you are ready to call this in your controller. In the controller, you need to inject the Dispatcher services and then use them in all the endpoints that are necessary.
To demonstrate the use, I created a controller called UsersController with a GetById method.
UsersController:
using Microsoft.AspNetCore.Mvc;
using MyAppCQRS.Application.Configuration;
using MyAppCQRS.Application.Dtos.Response.Users;
using MyAppCQRS.Application.UseCases.Users;
namespace MyAppCQRS.Api.Controllers;
[ApiController]
[Route("[controller]")]
public class UsersController : ControllerBase
{
private readonly IQueryDispatcher _queryDispatcher;
private readonly ICommandDispatcher _commandDispatcher;
public UsersController(
IQueryDispatcher queryDispatcher,
ICommandDispatcher commandDispatcher)
{
_queryDispatcher = queryDispatcher;
_commandDispatcher = commandDispatcher;
}
[HttpGet("{id}")]
public async Task GetById(Guid id, CancellationToken cancellationToken)
{
var query = new GetUserByIdQuery(id);
var user = await _queryDispatcher.Dispatch(query, cancellationToken);
if (user == null)
{
return NotFound();
}
return Ok(user);
}
}
Don't forget to add the declarations to inject the services into the dependency container in Program.cs.
Program.cs:
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped, GetUserByIdQueryHandler>();
Conclusion:
This is the end of this article. We have shown a very simplified way to have the main functionality of Jimmy Bogard's famous library. A way for you to implement CQRS without depending on an external library. You can create all your queries and commands as you wish, adding them to the necessary use cases. Add anything else you need, such as repository services, logs, etc.
Good work, see you in the next article!