我想装饰一下我的MediatR的IRequestHandlers。为了标记我想要修饰的处理程序,我创建了接口ICommandHandler,它继承自IRequestHandler,ICommand继承自IRequest。
public interface ICommand : IRequest { }
public interface ICommandHandler<in TCommand> : IRequestHandler<TCommand>
where TCommand : ICommand { }现在,我希望每个ICommandHandler都用UnitOfWorkCommandHandlerDecorator来装饰。
internal class UnitOfWorkCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand>
where TCommand : ICommand
{
private readonly ICommandHandler<TCommand> _decorated;
private readonly IUnitOfWork _unitOfWork;
public UnitOfWorkCommandHandlerDecorator(ICommandHandler<TCommand> decorated, IUnitOfWork unitOfWork)
{
_decorated = decorated;
_unitOfWork = unitOfWork;
}
public async Task<Unit> Handle(TCommand request, CancellationToken cancellationToken)
{
var res = await _decorated.Handle(request, cancellationToken);
await _unitOfWork.CommitAsync();
return res;
}
}我尝试用Scrutor的装饰方法注册一个装饰器,但是在用IMediator.Send发送请求时不会调用装饰器。这可能是因为它是ICommandHandler装饰师,而不是IRequestHandler装饰师。
services.Decorate(typeof(ICommandHandler<>), typeof(UnitOfWorkCommandHandlerDecorator<>));是否可以使用Microsoft,还是需要另一个DI容器?
发布于 2022-03-29 09:48:27
据我所读,使用Microsofts不可能在开放泛型上实现一个开放泛型的装饰。但是你可以用一个开放的通用来装饰一个封闭的泛型。这就产生了这样的想法:扫描程序集以查找泛型,找到关闭的实现,然后装饰它。
services.Scan(scan =>
scan.FromAssembliesOf(typeof(ICommandHandler<>))
.AddClasses(classes =>
classes.AssignableTo(typeof(ICommandHandler<>)).Where(_ => !_.IsGenericType))
.AsImplementedInterfaces()
.WithTransientLifetime());我不能为来自GitHub票证:https://github.com/khellang/Scrutor/issues/68的代码取得任何荣誉。
发布于 2022-04-18 18:53:47
正如我所预期的,当ICommandHandler从Microsoft请求IRequestHandler时,问题是如何装饰IRequestHandler。
我找到了两种方法来实现这个目标:
当ICommandHandler.请求
// Register all concrete classes implementing ICommandHandler as ICommandHandlers.
services.Scan(x => x.FromAssembliesOf(typeof(ICommand))
.AddClasses(classes => classes.AssignableTo(typeof(ICommandHandler<>))
.Where(t => !t.IsGenericType))
.AsImplementedInterfaces(t => t.IsGenericType
&& t.GetGenericTypeDefinition() == typeof(ICommandHandler<>))
.WithTransientLifetime());
// Decorate registered ICommandHandlers with open generic decorator.
services.Decorate(typeof(ICommandHandler<>), typeof(UnitOfWorkCommandHandlerDecorator<>));
// Get registered ICommandHandlers service descriptors from the container.
var registeredCommandHandlers = services
.Where(x => x.ServiceType.IsGenericType
&& x.ServiceType.GetGenericTypeDefinition() == typeof(ICommandHandler<>))
.ToArray();
foreach(var serviceDescriptor in registeredCommandHandlers)
{
// Find the IRequestHandler type of registered ICommandHandler.
var requestHandlerType = serviceDescriptor.ServiceType.GetInterfaces()
.First(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IRequestHandler<,>));
// Register a decorated ICommandHandler to be returned for IRequestHandler.
services.Add(new ServiceDescriptor(requestHandlerType,
sp => sp.GetRequiredService(serviceDescriptor.ServiceType), serviceDescriptor.Lifetime));
}
// Invoke AddMediatR method to register IRequestHandlers that are not ICommandHandlers
// and others required by MediatR classes.
services.AddMediatR(typeof(TestCommandHandler));IRequestHandler.
// Register all concrete classes implementing ICommandHandler as ICommandHandlers.
services.Scan(x => x.FromAssembliesOf(typeof(ICommand))
.AddClasses(classes => classes.AssignableTo(typeof(ICommandHandler<,>))
.Where(t => !t.IsGenericType))
.AsImplementedInterfaces(t => t.IsGenericType
&& t.GetGenericTypeDefinition() == typeof(ICommandHandler<,>))
.WithTransientLifetime());
// Decorate registered ICommandHandlers with open generic decorator.
services.Decorate(typeof(ICommandHandler<,>), typeof(UnitOfWorkCommandHandlerDecorator<,>));
// Invoke AddMediatR method to register IRequestHandlers that are not ICommandHandlers
// and others required by MediatR classes.
services.AddMediatR(typeof(TestCommandHandler));
// Register ServiceFactory delegate, which MediatR uses to resolve all services.
services.AddTransient<ServiceFactory>(sp =>
{
return t =>
{
if (t.IsGenericType
&& t.GetGenericTypeDefinition() == typeof(IRequestHandler<,>))
{
var genericArguments = t.GetGenericArguments();
var commandHandlerType = typeof(ICommandHandler<,>).MakeGenericType(genericArguments);
// Return ICommandHandler or IRequestHandler if it is not registered.
return sp.GetService(commandHandlerType)
?? sp.GetRequiredService(t);
}
return sp.GetRequiredService(t);
};
});就我个人而言,我更喜欢第一种方法;它更容易理解,而且在运行时没有那么多的开销。
https://stackoverflow.com/questions/71659557
复制相似问题