我可以在CQRS中的另一个CommandHandler中调用QueryHandler/CommandHandler吗

问题描述 投票:0回答:4

我正在开发一个 ASP.NET Core 6.0 Web API 项目。我使用 CQRS 设计模式。

我想更新

airlines
表(代码优先 EF Core)。所以首先我需要找到航空公司的ID。

我有

GetAirlineByIdQueryHandler.cs

public record GetAirlineByIdQuery(int Id, bool LoadOverview = true) : IRequest<Airline>;

public class GetAirlineByIdQueryHandler : IRequestHandler<GetAirlineByIdQuery, Airline>
{
    public async Task<Airline> Handle(GetAirlineByIdQuery request, CancellationToken cancellationToken)
    {
        var query = _techneDbContext.Airline
                .Include(d => d.X)
                .Include(d => d.Y)
                .Include(d => d.Z)
               
                .AsQueryable();

        if (request.LoadOverview)
        {
            query = query.Include(d => d.Overview);
        }

      var airline = await query.FirstOrDefaultAsync(d => d.Id == request.Id);


        if (airline == null)
        {
            throw new NotFoundException(nameof(Airline), request.Id);
        }

         return airline;
    }
}

更新AirlineCommand.cs

public class UpdateAirlineCommand : AirlineUpdateDto, IRequest<Airline>
{
    public int Id { get; set; }
}

public class UpdateAirlineCommanddHandler : IRequestHandler<UpdateAirlineCommand, Airline>
{
    // removed constructor

    public async Task<Airline> Handle(UpdateAirlineCommand request, CancellationToken cancellationToken)
    {
        // To update I have find the the id is there or not?
        // Can I call GetAirlineByIdQueryHandler here or do I need to copy the query and paste it here
    }
}
c# asp.net-core asp.net-core-webapi cqrs
4个回答
1
投票

通常命令和查询在不同的模型上运行。这为您提供了优化的读取和写入数据模式。在您的示例中,查询返回将由命令更新的相同模型。在命令中使用查询的另一个缺点是使它们紧密耦合。 CQRS 的巨大优势之一是将读端和写端分开。在您的示例中,GetAirlineByIdQueryHandler 中的任何更改都会影响 UpdateAirlineCommanddHandler。


0
投票

问题的答案是,不要这样做。您可以参考以下链接。

就我而言,我喜欢 DRY 原则,因此我使用 GetAirlineByIdQueryHandler 中的

extract class
解决重复代码,并创建
GetAirlineByIdHandler
处理程序或
Airline
服务来执行此操作。

// Extract class
public record GetAirlineById(int Id, bool LoadOverview = true);

publc interface IGetAirlineByIdHandler {
    public Task<Airline> Handle(GetAirlineById dto, CancellationToken cancellationToken);
}

publc class GetAirlineByIdHandler : IGetAirlineByIdHandler 
{
    // ...
    public async Task<Airline> Handle(GetAirlineById dto, CancellationToken cancellationToken)
    {
        // same logic with GetAirlineByIdQueryHandler 
    }
}

// Refactor
public record GetAirlineByIdQuery(int Id, bool LoadOverview = true) : GetAirlineById(Id, LoadOverview), IRequest<Airline>;

public class GetAirlineByIdQueryHandler : IRequestHandler<GetAirlineByIdQuery, Airline>
{
    private readonly IGetAirlineByIdHandler _getAirlineByIdHandler;
    // removed constructor
    
    public async Task<Airline> Handle(GetAirlineByIdQuery request, CancellationToken cancellationToken)
    {
        return await _getAirlineByIdHandler.Handle(request, cancellationToken);
    }
}

public class UpdateAirlineCommanddHandler : IRequestHandler<UpdateAirlineCommand, Airline>
{
    private readonly IGetAirlineByIdHandler _getAirlineByIdHandler;
    // removed constructor

    public async Task<Airline> Handle(UpdateAirlineCommand request, CancellationToken cancellationToken)
    {
        // To update I have find the the id is there or not?
        // Can I call GetAirlineByIdQueryHandler here or do I need to copy the query and paste it here
        _getAirlineByIdHandler.Handle(..., cancellationToken);
    }
}

0
投票

MediatR
模式中,不建议在另一个处理程序中调用一个处理程序,MediatR 提供了替代解决方案,例如:

  • 域名服务
  • 活动

在我看来,在

Infrastructure
中创建一个域服务,用于从 DB 获取机票,并将适当的实体返回给您的两个处理程序,即
UpdateAirlineCommanddHandler
GetAirlineByIdQueryHandler
。这是处理重复代码的更合理和推荐的方式。

了解更多详情👉 https://aspnetboilerplate.com/Pages/Documents/Domain-Services

如果需要

Events
的信息,请参阅👉https://www.rahulpnath.com/blog/avoid-commands-calling-commands/#comment-5444567877


0
投票

正如 @geek 所说,CQRS 的要点是写入模型和查询模型的分离。我认为您应该重新考虑使用 CQRS,因为它侧重于分离而不是共享逻辑。您正在寻找的是重用规格的规格模式。但这两者不可混用。阅读 Vladimir Khorikov 的这个链接了解更多信息:

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.