【问题标题】:Edit action CQRS pattern using MediatR使用 MediatR 编辑动作 CQRS 模式
【发布时间】:2020-09-18 14:39:44
【问题描述】:

我有一个 .Net Core 3.1 MVC 应用程序,我正在尝试使用 CQRS 模式。我是 MediatR 和 CQRS 模式的新手。

我的命令/查询结构是这样的:

  • 分类
    • 命令
      • 删除类别
      • UpsertCategory
    • 查询
      • 获取类别列表
        • CategoryListVm
        • 类别Dto
        • GetCategoriesListQuery
        • GetCategoriesListQueryHandler

我想使用相同的视图来创建和更新操作,因为我的类别只有两个属性。 (其中一个是Id)

我的 CategoryController.cs 文件

[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);
        }
    }

我的类别/索引视图在这里调用 upsert 方法。

<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>

我只想将 Id 信息发送到控制器,然后我如何/在哪里映射/到达 UpsertCategoryCommand。

所有其他型号:

    //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>();
            }
        }
    }

在同一页面上创建和编辑以及映射的最佳做法是什么?我可以在我的命令中使用 CategoryDto 吗?为命令和查询定义任何通用的 Dto,好吗?

【问题讨论】:

  • 我也有同样的问题。我对常见的 Dtos 感到困惑。

标签: c# asp.net-core cqrs mediatr


【解决方案1】:

我将为 CreateCategory 和 UpdateCategory 创建单独的命令,以便更清楚地了解用户的意图。我也认为这些命令的响应应该是单独的类型,并尽量避免在命令之间重用类。

我也只会在每个命令中包含真正必要的字段,而不是尝试在各种命令中重用 CategoryDto。

所以你会有一个 CreateCategoryReponse 和 UpdateCategoryResponse 类型。我也认为 Jimmy 在他最近的一次谈话中讨论了这个问题。

【讨论】:

  • 在我的情况下,类别只有 2 个属性。如果我创建 2 个不同的命令,我有很多重复的代码。如果我没有,我会解决重复代码问题,但是那是时候了,如果控制在命令中,我有一个额外的映射和不必要的。所以,我想我选择了重复的代码或额外的控制。
  • 实际上,在很多情况下,重复代码往往是一件好事......它消除了很多耦合和依赖......每个命令和处理程序都应该独立于其他命令/处理程序尽可能。
猜你喜欢
  • 2023-03-02
  • 2019-05-09
  • 2023-02-25
  • 2020-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-27
相关资源
最近更新 更多