【问题标题】:Coordinating Query And Command Handlers with Mediatr使用 Mediatr 协调查询和命令处理程序
【发布时间】:2020-10-10 00:45:49
【问题描述】:

我正在使用 Mediatr 在 dotnet core 3.0 中实现 CQRS 模式。我对如何协调不同的多个查询和命令有一些疑问。根据我在网上阅读的内容,这里有一些人们描述的最佳做法。

  • 查询处理程序不应依赖于命令处理程序
  • 命令处理程序不应依赖于查询处理程序
  • 不要使用装饰器,而是使用IPipelineBehaviour
  • 每个命令都应一对一映射到HTTP Request(例如:创建用户会将命令发送到CreateUserCommandHandler,这将负责所有工作)
  • 对于命令处理程序,返回 void 。使用 Mediatr 框架,我会返回 Task<Unit>

这是我的问题,现在,我正在为 AspNet.Identity 实现 IUserStore<TUser>,假设我们正在查看这个示例

    public async Task<IdentityResult> DeleteAsync(User user, CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();
        if (user == null) return IdentityResult.Failed(new ArgumentNullError(nameof(user)));
        
        bool userExists = await _mediator.Send(new UserExistsQuery {UserId = user.Id}, cancellationToken);
        
        if (!userExists) return IdentityResult.Failed(new EntityDoesNotExistError(user));
        
        await _mediator.Send(new DeleteUserCommand {User = user}, cancellationToken);
        return IdentityResult.Success;
    }

在这种方法中,我基本上是使用实现用户存储来协调不同的查询和命令以删除用户。在这种情况下,我可以看到执行以下操作以使其尽可能薄并更严格地遵循“每个 http 请求一个命令”

  1. 摆脱 UserExistsQuery 和 UserExistsQueryQueryHandler,将该查询移至 DeleteUserCommandHandler 并使用存储库进行查询(UserExistsQueryHandler 现在已经这样做了)而不是依赖于查询处理程序
  2. 不是返回 Task&lt;Unit&gt; ,而是返回类似 IdentityResult 的东西

我犹豫是否要执行 #2 的原因是,我感觉我正在根据使用命令的上下文返回一些东西。我返回一个 IdentityResult 只是因为我需要它来处理这个实例。

此外,我首先将其拆分成这样的原因是为了可重用性。我希望能够进行一些可以在其他地方重复使用的查询和命令。如果我返回一个IdentityResult,那么这种方式就达不到目的,因为除了UserStore&lt;TUser&gt;,我在任何地方都不需要它

我一直在阅读有关 IPipelineBehaviour 的信息,但它似乎更像是所有查询/命令处理程序的通用解决方案(即:如果您的程序集中存在适当的类型,则可以为每个命令/查询运行该管道)。但是IPipelineBehaviour 可以用来实现自定义管道吗?在我的示例中,我会将所有这些逻辑移至仅针对DeleteUserCommand 运行的管道?

我已经搜索过有关此主题的文章,但实际上找不到任何有用的东西 - 或者我正在搜索错误的术语。我的术语可能是错误的,但我可以创建仅依赖于IMediatr 的协调服务来完成删除用户。任何反馈和/或阅读材料将不胜感激。

【问题讨论】:

    标签: c# .net-core cqrs mediatr


    【解决方案1】:

    我认为您误解了 CQRS 的概念。 DeleteAsync 操作本身就是一个命令。所以如果你需要读取一些数据来继续你的操作,这不是一个查询,而只是一个读取操作。因此,每当您需要在命令操作中读取一些数据时,您都必须通过存储库获取它。 请注意,通过使用 CQRS,您可以将用户查询和命令的路径分开,而不是读取和写入。 在命令中,每次写入和读取都必须通过您的域模型。因此,您的域层中可能存在一个存储库来从您的写入数据库中获取数据。但是对于查询,没有必要使用域模型。 所以您的代码必须如下所示:

    public async Task<IdentityResult> DeleteAsync(User user, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            _mediator.Send(new DeleteUserCommand(){});       
        }
    

    DeleteUserCommand Handler 是这样的:

    protected override async Task Handle(DeleteUserCommand request, CancellationToken cancellationToken)
            {
                if (user == null) return IdentityResult.Failed(new ArgumentNullError(nameof(user)));
            
            bool userExists = userRepository.ExistUser(request.UserId);
            
            if (!userExists) return IdentityResult.Failed(new EntityDoesNotExistError(user));
            
            await userRepository.DeleteAsync(request.UserId);
            return IdentityResult.Success;
            }
    

    【讨论】:

      猜你喜欢
      • 2021-06-22
      • 2021-08-24
      • 2019-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-24
      • 2021-12-31
      相关资源
      最近更新 更多