You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用泛型构建MediatR聚合根查询处理器的类型推断问题

问题描述

在领域驱动设计(DDD)架构中尝试使用泛型为聚合根创建抽象查询请求处理器,搭建MediatR请求相关类后,调用ISender.Send方法时出现编译器错误——编译器将响应对象识别为object,而非预期的QueryResult<T>。

相关代码

IQuery接口

public interface IQuery<T> : IRequest<T>
    where T : class
{
    Guid CorrelationId { get; }
}

BaseResult抽象类

public abstract class BaseResult
{
    protected BaseResult(ResultStatus status, Dictionary<string, List<string>>? errors)
    {
        this.Status = status;
        this.Errors = errors ?? new Dictionary<string, List<string>>();
        this.IsSuccess = status == ResultStatus.Success;
    }

    public bool IsSuccess { get; private set; }
    public ResultStatus Status { get; private set; }
    public Dictionary<string, List<string>> Errors { get; private set; }

    // 省略其他代码
}

QueryResult泛型类

public class QueryResult<T> : BaseResult
    where T : class
{
    private QueryResult(T? data, ResultStatus status, Dictionary<string, List<string>>? errors)
        : base(status, errors)
    {
        this.Data = data;
    }

    public T? Data { get; }

    // 省略其他代码
}

AggregateRootQuery泛型类

public class AggregateRootQuery<TAggregate, TDto>
    : IRequest<QueryResult<IEnumerable<TDto>>>, IQuery<IEnumerable<TDto>>
    where TAggregate : class, IAggregateRoot // IAggregateRoot是标记接口,限制查询仅从聚合根开始
    where TDto : class
{
    public AggregateRootQuery(Guid correlationId)
    {
        this.CorrelationId = correlationId;
    }

    public Guid CorrelationId { get; }
}

BaseAggregateRootQueryHandler抽象类

public abstract class BaseAggregateRootQueryHandler<TAggregate, TDto> : IRequestHandler<AggregateRootQuery<TAggregate, TDto>, QueryResult<IEnumerable<TDto>>>
    where TAggregate : class, IAggregateRoot
    where TDto : class
{
    protected BaseAggregateRootQueryHandler(IDbContext dbContext, IMapper mapper, ILogger logger)
    {
        this.DbContext = dbContext;
        this.Mapper = mapper;
        this.Logger = logger;
    }

    protected IDbContext DbContext { get; }
    protected IMapper Mapper { get; }
    protected ILogger Logger { get; }

    public async Task<QueryResult<IEnumerable<TDto>>> Handle(AggregateRootQuery<TAggregate, TDto> request, CancellationToken cancellationToken)
    {
        var entities = await this.ApplyFilter(this.DbContext.AggregateRoot<TAggregate>())
            .ToArrayAsync(cancellationToken);

        var dtos = this.MapToDataTransferObjects(entities);

        this.Logger.Debug("{Count} {EntityType} read from database", entities.Length, nameof(TAggregate));

        return QueryResult<IEnumerable<TDto>>.Success(dtos);
    }

    protected abstract IQueryable<TAggregate> ApplyFilter(IQueryable<TAggregate> source);

    protected virtual IEnumerable<TDto> MapToDataTransferObjects(IEnumerable<TAggregate> source)
    {
        return this.Mapper.Map<IEnumerable<TDto>>(source);
    }
}

使用示例

具体查询处理器

// Domain.Order实现IAggregateRoot接口
public class OrderQueryHandler : BaseAggregateRootQueryHandler<Domain.Order, OrderDto>
{
    public OrderQueryHandler(IDbContext dbContext, IMapper mapper, ILogger logger)
        :base(dbContext, mapper, logger)
    {
    }

    protected override IQueryable<Domain.Order> ApplyFilter(IQueryable<Domain.Order> source)
    {
        return source.OrderBy(x => x.Name);
    }
}

调用代码(出现错误)

var result = await this.Mediator.Send(new AggregateRootQuery<Domain.Order, OrderDto>(Guid.NewGuid()));

// 编译错误:编译器认为result是object,无法访问IsSuccess和Data属性
if (!result.IsSuccess || result.Data == null)
{
    // 错误处理逻辑
}
错误原因与解决方案

错误原因

AggregateRootQuery<TAggregate, TDto>同时实现了IRequest<QueryResult<IEnumerable<TDto>>>和IQuery<IEnumerable<TDto>>,而IQuery<T>本身继承自IRequest<T>,这导致该类存在两个不同的IRequest接口实现:

  • IRequest<QueryResult<IEnumerable<TDto>>>
  • IRequest<IEnumerable<TDto>>(通过IQuery<IEnumerable<TDto>>继承)

MediatR的ISender.Send方法进行类型推断时,无法确定应该使用哪一个IRequest的返回类型,最终只能推断出最宽泛的object类型,引发编译错误。

另外,调用代码中存在笔误:OrdrDto应为OrderDto,但这不是类型推断失败的核心原因。

解决方案

方案1:移除重复的IRequest实现

既然IQuery<T>已经继承自IRequest<T>,AggregateRootQuery只需实现IQuery<QueryResult<IEnumerable<TDto>>>即可,无需显式实现IRequest接口:

修改AggregateRootQuery定义:

public class AggregateRootQuery<TAggregate, TDto>
    : IQuery<QueryResult<IEnumerable<TDto>>> // 仅实现IQuery接口,它已继承IRequest
    where TAggregate : class, IAggregateRoot
    where TDto : class
{
    public AggregateRootQuery(Guid correlationId)
    {
        this.CorrelationId = correlationId;
    }

    public Guid CorrelationId { get; }
}

BaseAggregateRootQueryHandler的泛型约束无需修改,原有逻辑保持正常运行。

方案2:显式指定Send方法的泛型参数

若不想修改接口结构,可在调用Send时显式指定返回类型,帮助编译器确定正确类型:

var result = await this.Mediator.Send<QueryResult<IEnumerable<OrderDto>>>(new AggregateRootQuery<Domain.Order, OrderDto>(Guid.NewGuid()));

此时编译器能明确识别返回类型为QueryResult<IEnumerable<OrderDto>>,可正常访问IsSuccess和Data属性。


内容的提问来源于stack exchange,提问作者TheMagnificent11

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 22:50:23