.NET 8 Series Starting Soon! Join the Waitlist

14 min read

Implementing CQRS with MediatR in ASP.NET Core - Ultimate Guide

#dotnet

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?

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.

Here is a diagramatic representaion on the basic flow of the CQRS Pattern.

cqrs-in-aspnet-core-3-1

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.

  1. Go to the class that we need an Interface for. In our case, go to the ApplicationContext.cs class.
  2. Select the entire class.
  3. Once selected, go to Edit -> Refactor -> Extract Interface.
  4. VS will ask for confirmation. Verify the name of the Interface to be generated and click Ok.
  5. Boom, we have our Interface ready. Imagine how helpful this feature will be when we have pretty long classes.

cqrs-in-aspnet-core-3-1

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

cqrs-in-aspnet-core-3-1

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.

cqrs-in-aspnet-core-3-1

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.

cqrs-in-aspnet-core-3-1

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.

cqrs-in-aspnet-core-3-1

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.

cqrs-in-aspnet-core-3-1

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.

cqrs-in-aspnet-core-3-1

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.

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.

Support ❤️
If you have enjoyed my content and code, do support me by buying a couple of coffees. This will enable me to dedicate more time to research and create new content. Cheers!
Share this Article
Share this article with your network to help others!

Boost your .NET Skills

I am starting a .NET 8 Zero to Hero Series soon! Join the waitlist.

Join Now

No spam ever, we are care about the protection of your data. Read our Privacy Policy