简体   繁体   中英

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

is the implementations of 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);
    }
}

And I want to create the generic 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 ... 
}

I'm trying to register a generic class as follows:

            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();

And throw error IQueryHandler has not been registered

Is this possible with Autofac?

Lots in common with Issue registering generic types with Autofac in ASP.NET Core

There's no decorators in the code you show.

The error you get is expected as you register your handlers by keying them - with the KeyedService class. If you want your code to work, you have 2 solutions.

The best option is to not key your services since there's no reason - again, only looking at the code you included - to key your services. You can register them without keying them this way:

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

The other option would be to instruct Autofac you're looking for a keyed service when you resolve the IQueryHandler<TQuery, TResult> since that's how you registered them. To do so, you'd have to modify your QueryDispatcher code to:

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

Please not the key passed to the ResolveKeyed method matches the one used in the registration code. I don't know why you'd choose the second option, but it's possible.

I assume you got a bit confused with 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 .

Edit after Edwok's comment

For the generic GetEntitiesQueryHandler<TEntity> case, I'd say you have to register it as an open-generic component :

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM