如何避免 MediatR 请求处理程序中的代码重复?
How to avoid code duplication in MediatR request handlers?
我正在研究 CQRS 和 MediatR 库,试图学习一些最佳实践。
我遇到的一个问题是 Command/Query 处理程序中的代码重复。我想知道在处理程序之间共享逻辑的最佳方式是什么。
示例:
我有一个定义 ID 属性 的抽象实体 class。所有实体都继承自 class。
public abstract class Entity
{
public long Id { get; private set; }
protected Entity(long id)
{
Id = id;
}
...
}
然后,我想为每个实体创建一个 GetById 查询。其中一个查询如下所示:
public class GetUserByIdQuery : IRequest<UserDto>
{
public long UserId { get; set; }
public class Handler : IRequestHandler<GetUserByIdQuery, UserDto>
{
private readonly IRepository<User> repository;
private readonly IMapper mapper;
public Handler(IUnitOfWork unitOfWork, IMapper mapper)
{
repository = unitOfWork.GetRepository<User>();
this.mapper = mapper;
}
public async Task<UserDto> Handle(GetUserByIdQuery request, CancellationToken cancellationToken)
{
var user = await repository.FindAsync(request.UserId, null, cancellationToken);
if (user is null)
{
throw new EntityNotFoundException();
}
return mapper.Map<UserDto>(user);
}
}
}
问题是这个 class 对于所有实体来说看起来完全一样。如果没有 CQRS,我可能会有这样的东西:
public class EntityFinder<TEntity, TDto> where TEntity : Entity
{
private readonly IRepository<TEntity> repository;
private readonly IMapper mapper;
public EntityFinder(IUnitOfWork unitOfWork, IMapper mapper)
{
repository = unitOfWork.GetRepository<TEntity>();
this.mapper = mapper;
}
public async Task<TDto> GetByIdAsync(long id)
{
var entity = await repository.FindAsync(id);
if (entity is null)
{
throw new EntityNotFoundException();
}
return mapper.Map<TDto>(entity);
}
}
我尝试用通用查询和处理程序做类似的事情,但 MediatR 找不到处理程序(即使我尝试手动将其注册到 DI 容器)。
避免此类重复的最佳方法是什么?
你能试试下面的代码吗?这样,您可以重用加载代码,同时提供一个端点来处理请求。
public class EntityFinder<TEntity, TDto> where TEntity : Entity
{ ... // Same as your code }
public class GetUserByIdQuery : IRequest<UserDto>
{
public long UserId { get; set; }
public class Handler : IRequestHandler<GetUserByIdQuery, UserDto>, EntityFinder<User, UserDto>
{
public Handler(IUnitOfWork unitOfWork, IMapper mapper) : base(unitOfWork, mapper)
{ }
public async Task<UserDto> Handle(GetUserByIdQuery request, CancellationToken cancellationToken)
=> await base.GetByIdAsync(request.UserId);
}
}
我正在研究 CQRS 和 MediatR 库,试图学习一些最佳实践。 我遇到的一个问题是 Command/Query 处理程序中的代码重复。我想知道在处理程序之间共享逻辑的最佳方式是什么。
示例: 我有一个定义 ID 属性 的抽象实体 class。所有实体都继承自 class。
public abstract class Entity
{
public long Id { get; private set; }
protected Entity(long id)
{
Id = id;
}
...
}
然后,我想为每个实体创建一个 GetById 查询。其中一个查询如下所示:
public class GetUserByIdQuery : IRequest<UserDto>
{
public long UserId { get; set; }
public class Handler : IRequestHandler<GetUserByIdQuery, UserDto>
{
private readonly IRepository<User> repository;
private readonly IMapper mapper;
public Handler(IUnitOfWork unitOfWork, IMapper mapper)
{
repository = unitOfWork.GetRepository<User>();
this.mapper = mapper;
}
public async Task<UserDto> Handle(GetUserByIdQuery request, CancellationToken cancellationToken)
{
var user = await repository.FindAsync(request.UserId, null, cancellationToken);
if (user is null)
{
throw new EntityNotFoundException();
}
return mapper.Map<UserDto>(user);
}
}
}
问题是这个 class 对于所有实体来说看起来完全一样。如果没有 CQRS,我可能会有这样的东西:
public class EntityFinder<TEntity, TDto> where TEntity : Entity
{
private readonly IRepository<TEntity> repository;
private readonly IMapper mapper;
public EntityFinder(IUnitOfWork unitOfWork, IMapper mapper)
{
repository = unitOfWork.GetRepository<TEntity>();
this.mapper = mapper;
}
public async Task<TDto> GetByIdAsync(long id)
{
var entity = await repository.FindAsync(id);
if (entity is null)
{
throw new EntityNotFoundException();
}
return mapper.Map<TDto>(entity);
}
}
我尝试用通用查询和处理程序做类似的事情,但 MediatR 找不到处理程序(即使我尝试手动将其注册到 DI 容器)。
避免此类重复的最佳方法是什么?
你能试试下面的代码吗?这样,您可以重用加载代码,同时提供一个端点来处理请求。
public class EntityFinder<TEntity, TDto> where TEntity : Entity
{ ... // Same as your code }
public class GetUserByIdQuery : IRequest<UserDto>
{
public long UserId { get; set; }
public class Handler : IRequestHandler<GetUserByIdQuery, UserDto>, EntityFinder<User, UserDto>
{
public Handler(IUnitOfWork unitOfWork, IMapper mapper) : base(unitOfWork, mapper)
{ }
public async Task<UserDto> Handle(GetUserByIdQuery request, CancellationToken cancellationToken)
=> await base.GetByIdAsync(request.UserId);
}
}