首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Microsoft装饰MediatR IRequestHandler

使用Microsoft装饰MediatR IRequestHandler
EN

Stack Overflow用户
提问于 2022-03-29 09:28:31
回答 2查看 434关注 0票数 0

我想装饰一下我的MediatR的IRequestHandlers。为了标记我想要修饰的处理程序,我创建了接口ICommandHandler,它继承自IRequestHandler,ICommand继承自IRequest。

代码语言:javascript
复制
public interface ICommand : IRequest { }
public interface ICommandHandler<in TCommand> : IRequestHandler<TCommand>
    where TCommand : ICommand { }

现在,我希望每个ICommandHandler都用UnitOfWorkCommandHandlerDecorator来装饰。

代码语言:javascript
复制
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装饰师。

代码语言:javascript
复制
services.Decorate(typeof(ICommandHandler<>), typeof(UnitOfWorkCommandHandlerDecorator<>));

是否可以使用Microsoft,还是需要另一个DI容器?

EN

回答 2

Stack Overflow用户

发布于 2022-03-29 09:48:27

据我所读,使用Microsofts不可能在开放泛型上实现一个开放泛型的装饰。但是你可以用一个开放的通用来装饰一个封闭的泛型。这就产生了这样的想法:扫描程序集以查找泛型,找到关闭的实现,然后装饰它。

代码语言:javascript
复制
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的代码取得任何荣誉。

票数 0
EN

Stack Overflow用户

发布于 2022-04-18 18:53:47

正如我所预期的,当ICommandHandler从Microsoft请求IRequestHandler时,问题是如何装饰IRequestHandler。

我找到了两种方法来实现这个目标:

当ICommandHandler.请求

  1. 时,让Microsoft DI返回一个修饰的IRequestHandler。

代码语言:javascript
复制
// 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.

  • 将MediatR更改为请求ICommandHandler而不是ICommandHandler

代码语言:javascript
复制
// 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);
    };
});

就我个人而言,我更喜欢第一种方法;它更容易理解,而且在运行时没有那么多的开销。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71659557

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档