Implementing CQRS with MediatR in ASP.NET Core – Ultimate Guide
In this article let’s talk about CQRS in ASP.NET Core 3.1 and it’s implementation along with MediatR and Entity Framework Core – Code First Approach. I will implement this pattern on a WebApi Project. The source code of this sample is linked at the end of the post. Of the several design patterns available, CQRS is one of the most commonly used patterns that helps architect the Solution to accommodate the Onion Architecture. I will be writing a post on Implementation of Onion Architecture too later, the cleanest way to structure a .NET Solution. So, Let’s get started!
- What is CQRS?
- Pros of CQRS
- Cons of CQRS
- Implementing CQRS Pattern in ASP.NET Core 3.1 WebApi
- Setting up the Project
- Adding the Product Model
- Adding the Context Class and Interface
- Configuring the API Services to support Entity Framework Core
- Defining the Connection String in appsettings.json
- Generating the Database
- The Mediator Pattern
- MediatR Library
- Configuring MediatR
- Creating the Product Controller
- Implementing the CRUD Operations
- Queries
- Commands
- Product Controller
- Testing
- Summary
- Frequently Asked Questions
What is CQRS?
CQRS, Command Query Responsibility Segregation is a design pattern that separates the read and write operations of a data source. Here Command refers to a Database Command, which can be either an Insert / Update or Delete Operation, whereas Query stands for Querying data from a source. It essentially separates the concerns in terms of reading and writing, which makes quite a lot of sense. This pattern was originated from the Command and Query Separation Principle devised by Bertrand Meyer. It is defined on Wikipedia as follows.
It states that every method should either be a command that performs an action, or a query that returns data to the caller, but not both. In other words, asking a question should not change the answer. More formally, methods should return a value only if they are referentially transparent and hence possess no side effects.
Wikipedia
The problem with traditional architectural patterns is that the same data model or DTO is used to query as well as update a data source. This can be the go-to approach when your application is related to just CRUD operations and nothing more. But when your requirements suddenly start getting complex, this basic approach can prove to be a disaster.
In practical applications, there is always a mismatch between the read and write forms of data, like the extra properties you may require to update. Parallel operations may even lead to data loss in the worst cases. That means, you will be stuck with just one Data Transfer Object for the entire lifetime of the application unless you choose to introduce yet another DTO, which in-turn may break your application architecture.
The idea with CQRS is to allow an application to work with different models. Long story short, you have one model that has data needed to update a record, another model to insert a record, yet another to query a record. This gives you flexibility with varying and complex scenarios. You don’t have to rely on just one DTO for the entire CRUD Operations by implementing CQRS.

Pros of CQRS
There are quite of lot of advantages on using the CQRS Pattern for your application. Few of them are as follows.
Optimised Data Transfer Objects
Thanks to the segregated approach of this pattern, we will no longer need those complex model classes within our application. Rather we have one model per data operation that gives us all the flexibility in the world.
Highly Scalable
Having control over the models in accordance with the type of data operations makes your application highly scalable in the long run.
Improved Performance
Practically speaking there are always 10 times more Read Operations as compared to the Write Operation. With this pattern you could speed up the performance on your read operations by introducing a cache or NOSQL Db like Redis or Mongo. CQRS pattern will support this usage out of the box, you would not have to break your head trying to implement such a cache mechanism.
Secure Parallel Operations
Since we have dedicated models per oprtation, there is no possibility of data loss while doing parellel operations.
Cons of CQRS
Added Complexity and More Code
The one thing that may concern a few programmers is that this is a code demanding pattern. In other words, you will end up with at least 3 or 4 times more code-lines than you usually would. But everything comes for a price. This according to me is a small price to pay while getting the awesome features and possibilities with the pattern.
Implementing CQRS Pattern in ASP.NET Core 3.1 WebApi
Let’ s build an ASP.NET Core 3.1 WebApi to showcase the implementation and better understand the CQRS Pattern. I will push the implemented solution over to Github, You can find the link to my repository at the end of this post. Let us build an API endpoint that does CRUD operations for a Product Entity, ie, Create / Delete / Update / Delete product record from the Database. Here, I use Entity Framework Core as the ORM to access data from my local DataBase.
PS – We will not be using any advanced architectural patterns, but let’s try to keep the code clean. The IDE I use is Visual Studio 2019 Community. If you do not have it, I totally recommend getting it. It’s completely free. Read the Installation Guide here.
Setting up the Project
Open up Visual Studio and Create a new ASP.NET Core Web Application with the WebApi Template.
Installing the required Packages
Install these following packages to your API project via the Package Manager Console. Just Copy Paste the below lines over to your Package Manager Console. All the required packages get installed. We will explore these packages as we progress.
Install-Package Microsoft.EntityFrameworkCore Install-Package Microsoft.EntityFrameworkCore.Relational Install-Package Microsoft.EntityFrameworkCore.SqlServer Install-Package MediatR Install-Package MediatR.Extensions.Microsoft.DependencyInjection Install-Package Swashbuckle.AspNetCore Install-Package Swashbuckle.AspNetCore.Swagger
Adding the Product Model
Since we are following a code first Approach, let’s design our data models. Add a Models Folder and create a new class named Product with the following properties.
public class Product { public int Id { get; set; } public string Name { get; set; } public string Barcode { get; set; } public bool IsActive { get; set; } = true; public string Description { get; set; } public decimal Rate { get; set; } public decimal BuyingPrice { get; set; } public string ConfidentialData { get; set; } }
Adding the Context Class and Interface
Make a new Folder called Context and add a class named Application Context. This particular class will help us to access the data using Entity Framework Core ORM.
public class ApplicationContext : DbContext { public DbSet<Product> Products { get; set; } public ApplicationContext(DbContextOptions<ApplicationContext> options) : base(options) { } public async Task<int> SaveChanges() { return await base.SaveChangesAsync(); } }
PRO TIP – How to Extract an Interface from a Class?
Now that we have completed the class, let me show you an easy way to generate an Interface for any given class. Visual Studio is much powerful than what we think it is. So here is how it goes.
- Go to the class that we need an Interface for. In our case, go to the ApplicationContext.cs class.
- Select the entire class.
- Once selected, go to Edit -> Refactor -> Extract Interface.
- VS will ask for confirmation. Verify the name of the Interface to be generated and click Ok.
- Boom, we have our Interface ready. Imagine how helpful this feature will be when we have pretty long classes.

Configuring the API Services to support Entity Framework Core
I have written an article on Entity Framework Core in ASP.NET Core 3.1. Do give it a look.
Navigate to your API Project’s Startup class. This is the class where the Application knows about various services and registrations required. Let’s add the support for EntityFrameworkCore. Just add these lines to your Startup Class’s ConfigureServices method. This will register the EF Core with the application.
services.AddDbContext<ApplicationContext>(options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection"), b => b.MigrationsAssembly(typeof(ApplicationContext).Assembly.FullName)));
Line 3 Says about the Connection String that is named as DefaultConnection. But we haven’t defined any such connection, have we?
Defining the Connection String in appsettings.json
We will need to connect a data source to the API. For this, we have to define a connection string in the appsettings.json found within the API Project. For demo purposes, I am using LocalDb Connection. You could scale it up to support multiple Database type. Various connection string formats can be found here.
Here is what you would add to your appsettings.json.
"ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=developmentDb;Trusted_Connection=True;MultipleActiveResultSets=true" },
Generating the Database
Now we have our models and the connection string ready, all we have to do is to generate a database from the defined models. For this, we have to use the Package Manager Console of Visual Studio. You can open this by going to Tools -> Nuget Package Manager -> Package Manager Console.
Before continuing, let’s check if there are any Build issues with the Solution. Build the Application once to ensure that there is no error, because it is highly possible for the next steps to not show any proper warning and fail if any errors exist.
Once the Build has succceeded, Let’s start by adding the migrations.
add-migration "initial"
After that, move on to update the database
update-database
You will get a Done Message once these operations are completed. As simple as that, we have got our database running in no time. This is exactly why including me, many developers prefer Entity Framework, It’s too easy and does not make any compromises in terms of performance or features. I am also planning to make an in-depth guide on EFCore later, which I will link up here once ready.
Let’s verify that the database has been created properly. Go to View -> SQL Server Object Explorer

The Database it properly created. Now, we will wire up this database to our API to perform CRUD Operations.
The Mediator Pattern
While building applications, especially ASP.NET Core Applications, it’s highly important to keep in mind that we always have to keep the code inside controllers as minimal as possible. Theoretically, Controllers are just routing mechanisms that take in a request and sends it internally to other specific services/libraries and return the data. It wouldn’t really make sense to put all your validations and logics within the Controllers.
Mediator pattern is yet another design pattern that dramatically reduces the coupling between various components of an application by making them communicate indirectly, usually via a special mediator object. We will dive deep into Mediator in another post. Essentially, the Mediator pattern is well suited for CQRS implementation.
MediatR Library
MediatR is a library that helps implements Mediator Pattern in .NET. The first thing we need to do, is to install the following packages.
Configuring MediatR
We have already installed the required package to our application. To register the library, add this line to the end of our API startup class in the ConfigureService Method.
services.AddMediatR(Assembly.GetExecutingAssembly());
Creating the Product Controller
To the Controllers Folder, Add a new Empty API Controller and name it ProductController.

Implementing the CRUD Operations
CRUD essentially stands for Create, Read, Update, and Delete. These are the Core components of RESTFul APIs. Let’s see how we can implement them using our CQRS Approach. Create a Folder named Features in the root directory of the Project and subfolders for the Queries and Command.

Queries
Here is where we will wire up the queries, ie, GetAllProducts and GetProductById. Make 2 Classes under the ProductFeatures / Queries Folder and name them GetAllProductsQuery and GetProductByIdQuery
Query to Get All Products
public class GetAllProductsQuery : IRequest<IEnumerable<Product>> { public class GetAllProductsQueryHandler : IRequestHandler<GetAllProductsQuery,IEnumerable<Product>> { private readonly IApplicationContext _context; public GetAllProductsQueryHandler(IApplicationContext context) { _context = context; } public async Task<IEnumerable<Product>> Handle(GetAllProductsQuery query, CancellationToken cancellationToken) { var productList = await _context.Products.ToListAsync(); if (productList == null) { return null; } return productList.AsReadOnly(); } } }
Line 1 suggests that we intend to return an IEnumerable list of Products from this Class implementing the IRequest interface of MediatR. Every request must have a request handler. Here it is mentioned in Line 3. At Line 10, we define how the request is being handled. This is almost the same for all types of requests and commands.
Query to Get Product By Id
public class GetProductByIdQuery : IRequest<Product> { public int Id { get; set; } public class GetProductByIdQueryHandler : IRequestHandler<GetProductByIdQuery, Product> { private readonly IApplicationContext _context; public GetProductByIdQueryHandler(IApplicationContext context) { _context = context; } public async Task<Product> Handle(GetProductByIdQuery query, CancellationToken cancellationToken) { var product = _context.Products.Where(a => a.Id == query.Id).FirstOrDefault(); if (product == null) return null; return product; } } }
Line #3 is the id of the product we need to fetch.
Line #13 , we query the database and fetch the record with Id as our query Id.
Commands
Add the following classes to ProductFeatures / Commands.
1.CreateProductCommand
2.DeleteProductByIdCommand
3.UpdateProductCommand
Command to Create a New Product
public class CreateProductCommand : IRequest<int> { public string Name { get; set; } public string Barcode { get; set; } public string Description { get; set; } public decimal BuyingPrice { get; set; } public decimal Rate { get; set; } public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand, int> { private readonly IApplicationContext _context; public CreateProductCommandHandler(IApplicationContext context) { _context = context; } public async Task<int> Handle(CreateProductCommand command, CancellationToken cancellationToken) { var product = new Product(); product.Barcode = command.Barcode; product.Name = command.Name; product.BuyingPrice = command.BuyingPrice; product.Rate = command.Rate; product.Description = command.Description; _context.Products.Add(product); await _context.SaveChanges(); return product.Id; } } }
Command to Delete a Product By Id
public class DeleteProductByIdCommand : IRequest<int> { public int Id { get; set; } public class DeleteProductByIdCommandHandler : IRequestHandler<DeleteProductByIdCommand, int> { private readonly IApplicationContext _context; public DeleteProductByIdCommandHandler(IApplicationContext context) { _context = context; } public async Task<int> Handle(DeleteProductByIdCommand command, CancellationToken cancellationToken) { var product = await _context.Products.Where(a => a.Id == command.Id).FirstOrDefaultAsync(); if (product == null) return default; _context.Products.Remove(product); await _context.SaveChanges(); return product.Id; } } }
Command to Update a Product
public class UpdateProductCommand : IRequest<int> { public int Id { get; set; } public string Name { get; set; } public string Barcode { get; set; } public string Description { get; set; } public decimal BuyingPrice { get; set; } public decimal Rate { get; set; } public class UpdateProductCommandHandler : IRequestHandler<UpdateProductCommand, int> { private readonly IApplicationContext _context; public UpdateProductCommandHandler(IApplicationContext context) { _context = context; } public async Task<int> Handle(UpdateProductCommand command, CancellationToken cancellationToken) { var product = _context.Products.Where(a => a.Id == command.Id).FirstOrDefault(); if (product == null) { return default; } else { product.Barcode = command.Barcode; product.Name = command.Name; product.BuyingPrice = command.BuyingPrice; product.Rate = command.Rate; product.Description = command.Description; await _context.SaveChanges(); return product.Id; } } } }
Product Controller
public class ProductController : ControllerBase { private IMediator _mediator; protected IMediator Mediator => _mediator ??= HttpContext.RequestServices.GetService<IMediator>(); [HttpPost] public async Task<IActionResult> Create(CreateProductCommand command) { return Ok(await Mediator.Send(command)); } [HttpGet] public async Task<IActionResult> GetAll() { return Ok(await Mediator.Send(new GetAllProductsQuery())); } [HttpGet("{id}")] public async Task<IActionResult> GetById(int id) { return Ok(await Mediator.Send(new GetProductByIdQuery { Id = id })); } [HttpDelete("{id}")] public async Task<IActionResult> Delete(int id) { return Ok(await Mediator.Send(new DeleteProductByIdCommand { Id = id })); } [HttpPut("{id}")] public async Task<IActionResult> Update(int id, UpdateProductCommand command) { if (id != command.Id) { return BadRequest(); } return Ok(await Mediator.Send(command)); } }
Testing
Since we are done with the implementation, let’s check the result. For this we use Swagger UI. We have already installed the package. Let’s configure it.
Configure Swagger
Add this piece of code to Startup.cs / ConfigureServices
#region Swagger services.AddSwaggerGen(c => { c.IncludeXmlComments(string.Format(@"{0}\CQRS.WebApi.xml", System.AppDomain.CurrentDomain.BaseDirectory)); c.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = "CQRS.WebApi", }); }); #endregion
Then, Add this to Configure method of Startup.cs
#region Swagger app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "CQRS.WebApi"); }); #endregion
Testing with Swagger
Now build the application and run it. Navigate to https://localhost:44311/swagger/. PS , your localhost port may vary. Navigate accordingly.

This is Swagger UI. It lets you document and test your APIs with great ease. To add a new product, click on the POST dropdown and add the details of the new product like below.

Once done, click on execute. This will try to add the specified record to the database. Now let’s see the list of products. Navigate to the GET method and click on execute. This is how easy things get with swagger.

Summary
We have covered CQRS implementation and definition, Mediator pattern and MediatR Library, Entity Framework Core Implementation, Swagger Integration, and much more. If I missed out on something or was not clear with the guide, let me know in the comments section below. Is CQRS your go-to approach for Complicated Systems? Let us know.
Source Code at Github
The finished source code of this implementation is available at Github.
Do follow me at GitHub as well
Frequently Asked Questions
What is the full form of CQRS?
CQRS stands for Command Query Responsibility Segregation. It’s a design pattern that seperated the read and write operations, hence decoupling the solution to a great extend.
Does CQRS Slow down your application?
No. CQRS may require a lot of code. But it does not hurt the performance at any level. With well written code, CQRS actually performs the data source calls in a better way.
Is CQRS a scalable design pattern?
Yes, CQRS is devised with scalability in mind.
You should also run “Install-Package Microsoft.EntityFrameworkCore.Tools” in the Nuget package Manager Console in order to be able to generate the migrations.
Yes, I believe this package is included by default while installing the other EF Core packages on ASP.NET Core Applications. You can refer to https://docs.microsoft.com/en-us/ef/core/get-started/install/#get-the-package-manager-console-tools . Thanks!
Is it necessary to check products for null in GetAllProductsQueryHandler.Handle(…) method?
Based on the source code of the ToListAsync(…) method, an empty collection will be returned instead of null
https://bit.ly/2YE5sOA
love it keep posting new articles made my day
I hope that some of the articles helped you. Thanks and Regards
Hi, good article!
Although I’m not sure that using reflection in the logging part is a good idea, especially in a high load scenario.
Hi, Yes I agree. It’s not apt to use Reflection. But since this is just a demonstration of pipeline and it has really nothing to do with logging, I tried to keep things simple. I had also added a small note to not use this in production. Thanks for the feedback.
Regards.
Hello! Can you provide also solution for Events on CQRS ?
Thank you very much for your work!
Hi, great article, I liked the way how you simplify things so everyone can understand. One thing to mention in your code basically the class Product is not model but it should be an entity
Great Article, Thank you.
great article, thank you
Hi,
The code below show a alert that it hides inherited member. Its really necessary ?
public async Task SaveChanges()
{
return await base.SaveChangesAsync();
}
@Jose I have this and it’s working fine, don’t forget
public async Task SaveChanges()
{
return await base.SaveChangesAsync();
}
BTW love the article, learning loads 🙂
@Jose
Add the generic int to the method signature. My last post stripped it out.
Wow. This article is article is pure gold. Thank you!
Hi! Thank you for your post! It’s absolutely clear and useful!
But one thing seems strange to me – duplication of ID in body and in the route of Update request. In my opinion, there is no reason for the Service user to specify ID twice.
Can you suggest solution to avoid it?
Thank you ! I really like the way you explain us the concepts …
You forget to install package EntityFrameworkCore..tool sir.
Yes, that’s a must to include the tools package.
I have to say I like this approach way more than standard service – repo approach.
Absolutely, this gives more control on the segregating the request and response and ultimately much more cleaner.
you must add this services.AddScoped();
Hi Mukesh,
This is a well-written article. I am implementing the code in ASP.NET 5. Everything is working fine except there is 1 error in ProductController at the line:
protected IMediator Mediator => _mediator ??= HttpContext.RequestServices.GetService();
Error CS0308 The non-generic method ‘IServiceProvider.GetService(Type)’ cannot be used with type arguments
Do you have any idea how to resolve this issue so that the project builds and runs?
Subsequent to my question earlier, all you need to do to remove the error is to include the using statement:
using Microsoft.Extensions.DependencyInjection;
Final changes to get the project running fine is:
Include in the ProductController class:
using Microsoft.Extensions.DependencyInjection;
Then in ProductController class you need to replace:
protected IMediator Mediator => _mediator ??= HttpContext.RequestServices.GetService();
with:
protected IMediator Mediator => _mediator ??= HttpContext.RequestServices.GetService(typeof(IMediator)) as IMediator;
Then in Startup class: (Register IApplicationContext) : services.AddScoped();
If anybody requires the full source code I am happy to share it on GitHub.
The correct code for ASP.NET 5 implementation for Handle Method will be :
You shall need to include, using Microsoft.EntityFrameworkCore;
public class GetProductByIdQuery : IRequest
{
public async Task Handle(GetProductByIdQuery query, CancellationToken cancellationToken)
{
var product = _context.Products.Where(a => a.Id == query.Id).FirstOrDefaultAsync();
if (product == null) return null;
return product;
}
}
}
How does this relate to your BlazorHero implementation? It seems they are quite different. Thanks.
This is a base implementation of CQRS features which is also implemented in the BlazorHero Project. You need to understand this with MediatR pattern to get the complete knowledge of the CQRS Pattern applied in the Application layer.
Regards
Hi,
I am getting this error (Asp.net Core 3.1):-
Error while validating the service descriptor ‘ServiceType: MediatR.IRequestHandler`2[CQRS.Api.Features.Queries.GetAllProductsQuery,System.Collections.Generic.IEnumerable`1[CQRS.Api.Models.Product]] Lifetime: Transient ImplementationType: CQRS.Api.Features.Queries.GetAllProductsQuery+GetAllProductsQueryHandler’: Unable to resolve service for type ‘CQRS.Api.Context.IApplicationContext’ while attempting to activate ‘CQRS.Api.Features.Queries.GetAllProductsQuery+GetAllProductsQueryHandler’.
However I have configured EF properly in statup class as mentioned in tutorial
services.AddDbContext(options =>
options.UseSqlServer(
Configuration.GetConnectionString(“DefaultConnection”),
b => b.MigrationsAssembly(typeof(ApplicationContext).Assembly.FullName)));
could you check if you have added in the IApplicationContext interface to the service container?
Thank you Mukesh for this great article.
Here’s an implementation of this article in ASP.NET Core 5.0 using MS SQL Server for write operations and Redis for read operations:
https://github.com/Ezeji/CQRS_Mediator.WebApi
Let me know your thoughts, I’d love to learn from you.
I am facing some issues while implementing authentication using JW Token, have you got any documentation for that, it will be very useful, moreover I would like to contribute your Fluent POS OS project, but getting started link is not working, if you can share your idea, I can work on the user interface part which supposed to be in angular
Hi, JWT Authentication is implemented in fluentpos as well. you can refer to it’s source code.
About getting-started, there is no separate document for it. But you can see the readme – https://github.com/fluentpos/fluentpos#readme. All the details are mentioned here.
If you are interested in contributing to fluentpos UI, could be reach me out on Discord. The links are on the readme as well.
Regards
I believe you need to install-package Microsoft.EntityFrameworkCore.Design to run add migration , thanks.
why Iam getting this warnings? new in this
1>C:\Users\001733\source\projects\CQRSwithMediatR\CQRSwithMediatR\Context\ApplicationContext.cs(16,32,16,43): warning CS0114: ‘ApplicationContext.SaveChanges()’ hides inherited member ‘DbContext.SaveChanges()’. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
1>C:\Users\001733\source\projects\CQRSwithMediatR\CQRSwithMediatR\Features\Queries\GetProductByIdQuery.cs(22,40,22,46): warning CS1998: This async method lacks ‘await’ operators and will run synchronously. Consider using the ‘await’ operator to await non-blocking API calls, or ‘await Task.Run(…)’ to do CPU-bound work on a background thread.
1>CQRSwithMediatR -> C:\Users\001733\source\projects\CQRSwithMediatR\CQRSwithMediatR\bin\Debug\net5.0\CQRSwithMediatR.dll
Addition of Microsoft.EntityFrameworkCore.Tools as additional nuget package to add to the solution. This is to alllow Migrations. Without it Migration command like “add-migration” and “Update-database” will not be recognized
I am new for CQRS. I do not understand why MediatR can implement CQRS, should this be done by separated database?
Everything is fine, but I have concerns about using the MediatR library. Why do we need it? Is it reliable? as it looks like magic and we are introducing “magic” in our code, Can’t we implement CQRS pattern without using MediatR.
This link has more food for thought around the above points
https://cezarypiatek.github.io/post/why-i-dont-use-mediatr-for-cqrs/
Nice article, it’s very helpful.Easy to understand concepts.Please keep on writing.
Thank you!!
How to perform xUnit Test Cases on these type of methods can you please suggest?