Trong bài viết này, mình sẽ đề cập đến CQRS Pattern và hướng dẫn triển khai một project WebAPI với MediatR, Entity Framework Core một cách dễ dàng, dễ hiểu và nhanh chóng.
![]() |
Triển khai CQRS Pattern với MediatR trong .NET Core |
CQRS là gì?
Mediator Pattern
Triển khai CQRS trong .NET Core
- MediatR
- MediatR.Extensions.Microsoft.DependencyInjection
- Microsoft.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.SqlServer
- Microsoft.EntityFrameworkCore.Tools
public class Product
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Detail { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
}
Bước 3: Tiếp theo, ta cần tạo Db Context, cụ thể bạn tạo thư mục Context và chứa 2 files lần lượt như sau:
public class ApplicationContext : DbContext
{
public ApplicationContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
public DbSet<Product> Products { get; set; }
}
Context/DataDbContextFactory.cs:
public class DataDbContextFactory : IDesignTimeDbContextFactory<ApplicationContext>
{
public ApplicationContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var connectionString = configuration.GetConnectionString("ConnectionString");
var optionsBuilder = new DbContextOptionsBuilder<ApplicationContext>();
optionsBuilder.UseSqlServer(connectionString);
return new ApplicationContext(optionsBuilder.Options);
}
}
Bước 4: Bây giờ bạn cần cấu hình chuỗi kết nối tới SQL Server trong appsetting.json:
"ConnectionStrings": {
"ConnectionString": "Server=HIUPC;Database=DbCQRSSolution;Trusted_Connection=True;MultipleActiveResultSets=true"
},
Bước 5: Ta vào Program.cs và thêm đoạn code sau bên trong:
builder.Services.AddDbContext<DataDbContext>(options => options.UseSqlServer(
builder.Configuration.GetConnectionString("ConnectionString")));
Bước 6: Vào Tools > NuGet Package Manager > NuGet Package Console và thực thi lần lượt 2 command sau:
- Add-Migration InitialCreate
- Update-database
![]() |
Triển khai CQRS Pattern với MediatR trong .NET Core |
public class GetAllProductsQuery : IRequest<IEnumerable<Product>>
{
public class GetAllProductsQueryHandler : IRequestHandler<GetAllProductsQuery, IEnumerable<Product>>
{
private readonly ApplicationContext _context;
public GetAllProductsQueryHandler(ApplicationContext 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();
}
}
}
Queries/GetProductByIdQuery.cs:
>public class GetProductByIdQuery : IRequest<Product>
{
public Guid Id { get; set; }
public class GetProductByIdQueryHandler : IRequestHandler<GetProductByIdQuery, Product>
{
private readonly ApplicationContext _context;
public GetProductByIdQueryHandler(ApplicationContext 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;
}
}
}
public class CreateProductCommand : IRequest<Guid>
{
public string Name { get; set; }
public string Detail { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand, Guid>
{
private readonly ApplicationContext _context;
public CreateProductCommandHandler(ApplicationContext context)
{
_context = context;
}
public async Task<Guid> Handle(CreateProductCommand command, CancellationToken cancellationToken)
{
var product = new Product();
product.Name = command.Name;
product.Detail = command.Detail;
product.Price = command.Price;
product.Quantity = command.Quantity;
_context.Products.Add(product);
await _context.SaveChangesAsync();
return product.Id;
}
}
}
Commands/UpdateProductCommand.cs:
public class UpdateProductCommand : IRequest<Guid>
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Detail { get; set; }
public decimal Price { get; set; }
public int Quantity { get; set; }
public class UpdateProductCommandHandler : IRequestHandler<UpdateProductCommand, Guid>
{
private readonly ApplicationContext _context;
public UpdateProductCommandHandler(ApplicationContext context)
{
_context = context;
}
public async Task<Guid> Handle(UpdateProductCommand command, CancellationToken cancellationToken)
{
var product = _context.Products.Where(a => a.Id == command.Id).FirstOrDefault();
if (product == null)
{
return default;
}
else
{
product.Name = command.Name;
product.Detail = command.Detail;
product.Price = command.Price;
product.Quantity = command.Quantity;
await _context.SaveChangesAsync();
return product.Id;
}
}
}
}
Commands/DeleteProductByIdCommand.cs:
public class DeleteProductByIdCommand : IRequest<Guid>
{
public Guid Id { get; set; }
public class DeleteProductByIdCommandHandler : IRequestHandler<DeleteProductByIdCommand, Guid>
{
private readonly ApplicationContext _context;
public DeleteProductByIdCommandHandler(ApplicationContext context)
{
_context = context;
}
public async Task<Guid> 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.SaveChangesAsync();
return product.Id;
}
}
}
Việc thực hiện các Commands cũng giống như tạo các queries ở trên, ở đây bạn cũng viết các Handler tướng ứng trong mỗi file tương ứng.
[Route("api/product")]
[ApiController]
public class ProductsController : ControllerBase
{
private readonly IMediator _mediator;
public ProductsController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet]
public async Task<IActionResult> Get()
{
return Ok(await _mediator.Send(new GetAllProductsQuery()));
}
[HttpGet("{id}")]
public async Task<IActionResult> GetById(Guid id)
{
return Ok(await _mediator.Send(new GetProductByIdQuery { Id = id }));
}
[HttpPost]
public async Task<IActionResult> Create(CreateProductCommand command)
{
return Ok(await _mediator.Send(command));
}
[HttpPut("{id}")]
public async Task<IActionResult> Update(Guid id, UpdateProductCommand command)
{
if (id != command.Id)
{
return BadRequest();
}
return Ok(await _mediator.Send(command));
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(Guid id)
{
return Ok(await _mediator.Send(new DeleteProductByIdCommand { Id = id }));
}
}
Bước 10: Bây giờ, ta cần cấu hình dependency injection. Ở các phiện bản trước .NET 6, bạn sẽ thêm trong ConfigureServices(), và ở .NET 6 thì bạn cấu hình như sau trong Program.cs:
builder.Services.AddMediatR(typeof(Program).Assembly);
Các bước thực hiện đã hoàn tất, bây giờ bạn chỉ cần run project và tận hưởng kết quả.
Lời kết
Mong bài viết hữu ích đến các bạn, chúc các bạn thành công.Hieu Ho.