IMediatR - 通用请求的通用请求处理程序
IMediatR - Generic Request Handler for generic Requests
我目前遇到 IMediatR IRequestHandlers 和 IRequest 的问题。为了提供一点上下文,在我们的工具上,用户可以通过填写几个字段来请求访问该工具。目前,我们有两个级别的访问权限,这意味着我们应该有两个 IRequestHandlers,一个对应一个。由于我们要做的只是将访问请求注册到数据库并发送电子邮件,我认为可以创建一个通用的 IRequestHandler 来处理这些情况(如果我们创建移动级别的访问,它将在未来有所帮助)。
我对如何通用注入这些类型的处理程序进行了一些研究,但我只能找到非通用注入。我想知道是否有办法为我在下面展示的那些 IRequests 注入那些类型的通用 IRequestHandlers?
这是我为 IRequest 创建的代码。
public class CreateAccessRequest<TViewModel> : IRequest<OperationResult<long>>, IValidatable
where TViewModel : AccessRequestViewModel
{
public TViewModel Request { get; set; }
}
这是请求处理程序,我可以创建的更通用的处理程序。
public class CreateAccessRequestHandler<TViewModel, TNotificationModel, TEntity> : IRequestHandler<CreateAccessRequest<TViewModel>, OperationResult<long>>
where TViewModel : AccessRequestViewModel
where TEntity : Entity
where TNotificationModel : INotification
{
private readonly IRepository<TEntity> _accessRequestRepository;
private readonly IMediator _mediator;
private readonly IMapper _mapper;
public CreateAccessRequestHandler(IRepository<TEntity> accessRequestRepository, IMediator mediator, IMapper mapper)
{
// initalization ...
}
public async Task<OperationResult<long>> Handle(CreateAccessRequest<TViewModel> request, CancellationToken cancellationToken)
{
// mapping, saving to database and sending notifications...
}
}
最后,这是处理程序的注入代码
void ConfigureMediatR(Container container)
{
var assemblies = GetAssemblies().ToArray();
container.RegisterSingleton<IMediator, Mediator>();
container.Register(typeof(IRequestHandler<,>), assemblies);
}
我正在这样创建请求:
// CreateApplicationAccessRequestViewModel inherits AccessRequestViewModel
var request = new CreateAccessRequest<CreateApplicationAccessRequestViewModel>();
MediatR 未找到请求的请求处理程序并引发此异常:
Error constructing handler for the request of type MediatR.IRequestHandler<CreateAccessRequest<CreateApplicationAccessRequestViewModel>, OperationResult<long>>. Register your handlers with the container. See the samples in GitHub for example.
我无法摆脱那个实现,我不得不创建一个 class 继承自 CreateAccessRequestHandler
并具有正确的通用类型,然后它起作用了:
public sealed class CreateApplicationAccessRequestHandler : CreateAccessRequestHandler<CreateApplicationAccessRequestViewModel, ApplicationAccessRequestNotificationModel, ApplicationAccessRequest>
{
public CreateApplicationAccessRequestHandler(IRepository<ApplicationAccessRequest> accessRequestRepository, IMediator mediator, IMapper mapper)
: base(accessRequestRepository, mediator, mapper)
{ }
}
我的想法是只使用通用处理程序,而不需要为每个访问请求类型创建派生的 classes。有办法吗?
--- 编辑: ---
我使用了史蒂文在他的回答中放置的代码,它几乎适用于我的情况。我将在下面解释这个 almost:
var types = container.GetTypesToRegister(typeof(IRequestHandler<,>), assemblies,
new TypesToRegisterOptions { IncludeGenericTypeDefinitions = true });
container.Register(typeof(IRequestHandler<,>), types.Where(t => !t.IsGenericTypeDefinition));
foreach(var openGenericType in types.Where(t => t.IsGenericTypeDefinition)) {
container.Register(typeof(IRequestHandler<,>), openGenericType);
}
我分别注册了泛型和非泛型类型,它在大多数情况下都有效,除了当你有一个无法解析的类型时,这就是我的情况 TEntity.
SimpleInjector 不知道如何解析 TEntity,因为(我猜)我没有在定义中将它提供给 IRequestHandler。
如果我在请求中稍微放置 TEntity(即 CreateAccessRequest<TViewModel, TEntity>
),它会起作用,但它会破坏项目的架构。所以我选择了我在这里提到的备份实现(从那些泛型中创建具体的处理程序)以至少保持代码集中。
MediatR didn't find the request handler to the request
MediatR 只是一堆接口;它不负责寻找请求处理程序——Simple Injector 是。
您目前正在使用以下代码注册您的处理程序:
container.Register(typeof(IRequestHandler<,>), assemblies);
这将注册:
all concrete, non-generic, public and internal types in the given set of assemblies
that implement the given openGenericServiceType
with container's default lifestyle
(如 XML 文档所述)
您要做的是不仅要注册非泛型,还要注册 泛型 类型。这可以通过多种方式实现。例如通过附加那些通用类型:
container.Register(typeof(IRequestHandler<,>), assemblies);
container.Register(typeof(IRequestHandler<,>), typeof(CreateAccessRequestHandler<,>));
但是,如果您有许多开放通用实现,批量注册它们可能更有效:
var types =
container.GetTypesToRegister(typeof(IRequestHandler<,>), assemblies,
new TypesToRegisterOptions { IncludeGenericTypeDefinitions = true });
container.Register(typeof(IRequestHandler<,>), types);
我目前遇到 IMediatR IRequestHandlers 和 IRequest 的问题。为了提供一点上下文,在我们的工具上,用户可以通过填写几个字段来请求访问该工具。目前,我们有两个级别的访问权限,这意味着我们应该有两个 IRequestHandlers,一个对应一个。由于我们要做的只是将访问请求注册到数据库并发送电子邮件,我认为可以创建一个通用的 IRequestHandler 来处理这些情况(如果我们创建移动级别的访问,它将在未来有所帮助)。
我对如何通用注入这些类型的处理程序进行了一些研究,但我只能找到非通用注入。我想知道是否有办法为我在下面展示的那些 IRequests 注入那些类型的通用 IRequestHandlers?
这是我为 IRequest 创建的代码。
public class CreateAccessRequest<TViewModel> : IRequest<OperationResult<long>>, IValidatable
where TViewModel : AccessRequestViewModel
{
public TViewModel Request { get; set; }
}
这是请求处理程序,我可以创建的更通用的处理程序。
public class CreateAccessRequestHandler<TViewModel, TNotificationModel, TEntity> : IRequestHandler<CreateAccessRequest<TViewModel>, OperationResult<long>>
where TViewModel : AccessRequestViewModel
where TEntity : Entity
where TNotificationModel : INotification
{
private readonly IRepository<TEntity> _accessRequestRepository;
private readonly IMediator _mediator;
private readonly IMapper _mapper;
public CreateAccessRequestHandler(IRepository<TEntity> accessRequestRepository, IMediator mediator, IMapper mapper)
{
// initalization ...
}
public async Task<OperationResult<long>> Handle(CreateAccessRequest<TViewModel> request, CancellationToken cancellationToken)
{
// mapping, saving to database and sending notifications...
}
}
最后,这是处理程序的注入代码
void ConfigureMediatR(Container container)
{
var assemblies = GetAssemblies().ToArray();
container.RegisterSingleton<IMediator, Mediator>();
container.Register(typeof(IRequestHandler<,>), assemblies);
}
我正在这样创建请求:
// CreateApplicationAccessRequestViewModel inherits AccessRequestViewModel
var request = new CreateAccessRequest<CreateApplicationAccessRequestViewModel>();
MediatR 未找到请求的请求处理程序并引发此异常:
Error constructing handler for the request of type MediatR.IRequestHandler<CreateAccessRequest<CreateApplicationAccessRequestViewModel>, OperationResult<long>>. Register your handlers with the container. See the samples in GitHub for example.
我无法摆脱那个实现,我不得不创建一个 class 继承自 CreateAccessRequestHandler
并具有正确的通用类型,然后它起作用了:
public sealed class CreateApplicationAccessRequestHandler : CreateAccessRequestHandler<CreateApplicationAccessRequestViewModel, ApplicationAccessRequestNotificationModel, ApplicationAccessRequest>
{
public CreateApplicationAccessRequestHandler(IRepository<ApplicationAccessRequest> accessRequestRepository, IMediator mediator, IMapper mapper)
: base(accessRequestRepository, mediator, mapper)
{ }
}
我的想法是只使用通用处理程序,而不需要为每个访问请求类型创建派生的 classes。有办法吗?
--- 编辑: ---
我使用了史蒂文在他的回答中放置的代码,它几乎适用于我的情况。我将在下面解释这个 almost:
var types = container.GetTypesToRegister(typeof(IRequestHandler<,>), assemblies,
new TypesToRegisterOptions { IncludeGenericTypeDefinitions = true });
container.Register(typeof(IRequestHandler<,>), types.Where(t => !t.IsGenericTypeDefinition));
foreach(var openGenericType in types.Where(t => t.IsGenericTypeDefinition)) {
container.Register(typeof(IRequestHandler<,>), openGenericType);
}
我分别注册了泛型和非泛型类型,它在大多数情况下都有效,除了当你有一个无法解析的类型时,这就是我的情况 TEntity.
SimpleInjector 不知道如何解析 TEntity,因为(我猜)我没有在定义中将它提供给 IRequestHandler。
如果我在请求中稍微放置 TEntity(即 CreateAccessRequest<TViewModel, TEntity>
),它会起作用,但它会破坏项目的架构。所以我选择了我在这里提到的备份实现(从那些泛型中创建具体的处理程序)以至少保持代码集中。
MediatR didn't find the request handler to the request
MediatR 只是一堆接口;它不负责寻找请求处理程序——Simple Injector 是。
您目前正在使用以下代码注册您的处理程序:
container.Register(typeof(IRequestHandler<,>), assemblies);
这将注册:
all concrete, non-generic, public and internal types in the given set of
assemblies
that implement the givenopenGenericServiceType
with container's default lifestyle
(如 XML 文档所述)
您要做的是不仅要注册非泛型,还要注册 泛型 类型。这可以通过多种方式实现。例如通过附加那些通用类型:
container.Register(typeof(IRequestHandler<,>), assemblies);
container.Register(typeof(IRequestHandler<,>), typeof(CreateAccessRequestHandler<,>));
但是,如果您有许多开放通用实现,批量注册它们可能更有效:
var types =
container.GetTypesToRegister(typeof(IRequestHandler<,>), assemblies,
new TypesToRegisterOptions { IncludeGenericTypeDefinitions = true });
container.Register(typeof(IRequestHandler<,>), types);