如何为 CQRS 的通用对象注册 autofac 装饰器

How to register an autofac decorator for a generic object for CQRS

是CQRS的实现:

public interface IQuery<TResult> {}

public interface IQueryHandler<in TQuery, TResult>
    where TQuery : IQuery<TResult>
{
    Task<TResult> HandleAsync(TQuery query);
}

public interface IQueryDispatcher
{
    Task<TResult> DispatchAsync<TQuery, TResult>(TQuery query)
        where TQuery : IQuery<TResult>;
}

public class QueryDispatcher : IQueryDispatcher
{
    private readonly IComponentContext resolver;

    public QueryDispatcher(IComponentContext resolver)
    {
        if (resolver == null)
        {
            throw new ArgumentNullException(nameof(resolver));
        }

        this.resolver = resolver;
    }

    public async Task<TResult> DispatchAsync<TQuery, TResult>(TQuery query) 
        where TQuery : IQuery<TResult>
    {
        if (query == null)
        {
            throw new ArgumentNullException(nameof(query));
        }

        var handler = resolver.Resolve<IQueryHandler<TQuery, TResult>>();
        return await handler.HandleAsync(query);
    }
}

我想创建通用查询:

public class GetEntitiesQuery<TEntity> : IQuery<IQueryable<TEntity>>
    where TEntity : Entity
{
}

public class GetEntitiesQueryHandler<TEntity> : IQueryHandler<GetEntitiesQuery<TEntity>, IQueryable<TEntity>>
    where TEntity : Entity
{
    // this code ... 
}

我正在尝试按如下方式注册通用 class:

            builder.RegisterType<QueryDispatcher>().As<IQueryDispatcher>().InstancePerLifetimeScope();

        builder.RegisterAssemblyTypes(assemblies)
            .As(type => type.GetInterfaces()
                .Where(interfaceType => interfaceType.IsClosedTypeOf(typeof(IQueryHandler<,>)))
                .Select(interfaceType => new KeyedService("QueryHandler", interfaceType)))
            .InstancePerLifetimeScope();

并抛出错误 IQueryHandler has not been registered

这可以用 Autofac 实现吗?

有很多共同点

您显示的代码中没有装饰器。

当您通过 键控 注册您的处理程序时,您得到的错误是预期的 - 使用 KeyedService class。如果你想让你的代码正常工作,你有 2 个解决方案。

最好的选择是不为您的服务设置密钥,因为没有理由(同样,仅查看您包含的代码)为您的服务设置密钥。您可以通过以下方式 keying 注册它们:

builder
    .RegisterAssemblyTypes(assemblies)
    .AsClosedTypesOf(typeof(IQueryHandler<,>))
    .InstancePerLifetimeScope();

另一种选择是在解析 IQueryHandler<TQuery, TResult> 时指示 Autofac 您正在寻找 keyed 服务,因为这就是您注册它们的方式。为此,您必须将 QueryDispatcher 代码修改为:

var handler = resolver.ResolveKeyed<IQueryHandler<TQuery, TResult>>("QueryHandler");

请不要将传递给 ResolveKeyed 方法的密钥与注册码中使用的密钥相匹配。我不知道你为什么会选择第二个选项,但这是可能的。

我想你对 decorators. Using them requires you to key your services so you can them decorate services that have been keyed. Please have another look at the relevant documentation 有点困惑。

根据 Edwok 的评论进行编辑

对于一般的 GetEntitiesQueryHandler<TEntity> 情况,我会说您必须将其注册为 open-generic component:

builder
    .RegisterGeneric(typeof(GenericEntitiesQueryHandler<>))
    .As(typeof(IQueryHandler<,>));