I have a .Net Core 3.1 MVC application and I am trying to use CQRS pattern. I am new in MediatR and CQRS pattern.
My commmand/query structure like that:
- Categories
- Commands
- DeleteCategory
- UpsertCategory
- Queries
- GetCategoriesList
- CategoryListVm
- CategoryDto
- GetCategoriesListQuery
- GetCategoriesListQueryHandler
- GetCategoriesList
- Commands
I want to use same view to create and update operation because my category has only two properties. (One of them is Id)
My CategoryController.cs file
[HttpGet]
public IActionResult Upsert(long? id) {
if (id == null) {
//Create new, working correctly.
return View();
}
//Update existing not working.
return View(new UpsertCategoryCommand() { Id = id });
}
[HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesDefaultResponseType]
public async Task<IActionResult> Upsert(UpsertCategoryCommand command) {
if (ModelState.IsValid) {
await Mediator.Send(command);
return RedirectToAction(nameof(Index));
} else {
return View(command);
}
}
My Category/Index view call upsert method in here.
<tbody>
@foreach (var item in Model.Categories) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.ActionLink("Edit", "Upsert", new { Id = item.Id }) |
@Html.ActionLink("Delete", "Delete", new { Id = item.Id})
</td>
</tr>
}
</tbody>
I want to send only Id information to controller and then how/where I map/get to UpsertCategoryCommand.
All other models:
//UpsertCategoryCommand.cs
namespace ...Categorys.Commands.UpsertCategory {
public class UpsertCategoryCommand : IRequest<long> {
public long? Id { get; set; }
public string Name { get; set; }
public class UpsertCategoryCommandHandler : IRequestHandler<UpsertCategoryCommand, long> {
private readonly ITestDbContext _context;
public UpsertCategoryCommandHandler(ITestDbContext context) {
_context = context;
}
public async Task<long> Handle(UpsertCategoryCommand request, CancellationToken cancellationToken) {
Category entity;
if (request.Id.HasValue) {
entity = await _context.Categories.FindAsync(request.Id.Value);
} else {
entity = new Category();
_context.Categories.Add(entity);
}
entity.Name = request.Name;
await _context.SaveChangesAsync(cancellationToken);
return entity.Id;
}
}
}
}
//UpsertCategoryCommandValidator.cs
namespace ...Categories.Commands.UpsertCategory {
public class UpsertCategoryCommandValidator : AbstractValidator<UpsertCategoryCommand> {
private readonly ITestDbContext _context;
public UpsertCategoryCommandValidator(ITestDbContext context) {
_context = context;
RuleFor(x => x.Name).MaximumLength(100).NotEmpty();
RuleFor(x => x.Name)
.Must(UniqueName)
.WithMessage("Category name must be unique."); ;
}
private bool UniqueName(UpsertCategoryCommand category, string name) {
var dbCategory = _context.Categories
.Where(x => x.Name.ToLower() == name.ToLower())
.SingleOrDefault();
if (dbCategory == null)
return true;
return dbCategory.Id == category.Id;
}
}
}
//CategoryDto.cs
namespace ...Categories.Queries.GetCategoryList {
public class CategoryDto : IMapFrom<Category> {
public long Id { get; set; }
public string Name { get; set; }
public void Mapping(Profile profile) {
profile.CreateMap<Category, CategoryDto>();
}
}
}
What is the best practies to doing same page for create and edit and also mapping? Could I use CategoryDto in my commands? Define to any common Dto for commands and queries, is good?
