【问题标题】:How to keep the command handler clean of querying logic? (CQRS and MediatR)如何保持命令处理程序的查询逻辑干净? (CQRS 和 MediatR)
【发布时间】:2021-10-11 11:36:48
【问题描述】:

我正在处理的问题是如何让我的控制器免于查询真正应该是基础架构而不是应用程序关注的逻辑。

下面是 Command 和 CommandHandler 的示例。顺便说一句,我正在使用 MediatR。

如您所见,Command 包含一些过滤器(我认为没问题,不违反 CQRS),但 Command Handler 中包含太多逻辑。我不确定这是处理程序必须处理的事情。

GetMatchesCommand

public class GetMatchesCommand : IRequest<IEnumerable<MatchDto>>
{
    public Tuple<DateTime, DateTime> TimeInterval { get; set; }
    public Guid? Location { get; set; }
    public Guid? Team { get; set; }
    public Guid? Player { get; set; }
}

GetMatchesCommandHandler

public class GetMatchesCommandHandler : IRequestHandler<GetMatchesCommand, IEnumerable<MatchDto>>
{       
    public async Task<IEnumerable<MatchDto>> Handle(GetMatchesCommand request, CancellationToken cancellationToken)
    {
        Expression<Func<Match, bool>> filterExpression = default;
            
        // Build the filter expression

        // Filter by interval
        if (request.TimeInterval != null)
            filterExpression.ConcatAnd(
                m => m.StartTime >= request.TimeInterval.Item1
                && (m.StartTime + m.Duration) <= request.TimeInterval.Item2);

        // Filter by team
        if (request.Team != null)
            filterExpression.ConcatAnd(
                m => m.Team1Id == request.Team
                || m.Team2Id == request.Team);

        // Filter by player
        if (request.Player != null)
            filterExpression.ConcatAnd(
                m => m.Team1.Players.Any(p => p.Id == request.Player)
                || m.Team2.Players.Any(p => p.Id == request.Player));

        var query = _dbContext.Matches
            .Include(m => m.Team1).ThenInclude(t => t.Players)
            .Include(m => m.Team2).ThenInclude(t => t.Players);

        // if there are any filters, apply them
        if(filterExpression != null)
        {
            query.Where(filterExpression);
        }

        var matches = query.ToListAsync();
        return _mapper.Map<List<MatchDto>>(matches);
    }
}

我知道存储库模式可能适合这种情况,但是,正确的做法是什么?老实说,拥有_matchesRepo.Get(interval, team, player, location) 并将此逻辑移到那里似乎并不是一个非常聪明的方法...

有人可以给我一个建议吗?提前谢谢!

【问题讨论】:

  • 当然我们可以做完整的企业,但你的目标是什么?如果我们遵守 80/20 规则,也就是说,您可以通过 20% 的努力获得 80% 的结果,而通过 80% 的努力获得剩下的 20% 的结果——这值得吗?将过滤逻辑提取到某种表达式构建器中,以便可以单独对其进行测试并使用它来完成。
  • 实际问题是我有多个这样的命令处理程序。顺便说一句,什么是表达式生成器?我认为这对我来说是新事物。我要去寻找那个。如果你能分享一些关于这些的资源,将不胜感激
  • 如果这些表达式在处理程序之间不一样,那么你真的没有什么可提取的。我可以通过将表达式提取到一个方法中来简化这个,这样你就可以写var filterExpression = GetExpression(request);

标签: c# .net cqrs clean-architecture mediatr


【解决方案1】:
根据您在此处提供的示例,

GetMatches 可能应该被理解为一个查询,而不是一个命令

关键提示是您的实现实际上是只读的 - 您没有对模型进行更改并将它们保存在存储库中。

在查询处理程序中进行过滤是很正常的事情。

您可以通过将过滤器表达式的构造封装到另一个函数中来简化这里的设计(关注点分离/信息隐藏)。

【讨论】:

    【解决方案2】:

    我正在做类似的足球比赛项目,你能分享你在设计数据库和UI方面的代码和经验吗?

    你完成了吗?我还想编写相同的函数并在用户在文本框上输入时使用 AJAX 获取结果(匹配列表),但是有人告诉我当您在每次按钮按下事件(用户再输入一个字符)上向服务器发送请求时的性能

    谢谢。

    【讨论】:

      猜你喜欢
      • 2021-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-24
      • 1970-01-01
      • 2022-01-08
      相关资源
      最近更新 更多