Taras
Taras

Reputation: 235

.NET Core dependency injection, resolve generic interface

I am having trouble with asp.net core dependency injection, I cannot resolve generic interface from IServiceProvider. Here is my setup:

Generic interfaces:

public interface IRequest<out TResponse> {...}

public interface IRequestHandler<TRequest, TResult> 
    where TRequest : IRequest<TResult> {...}

Concrete implementation:

public class GetUsersQuery : IRequest<IEnumerable<GetUsersResult>> {...}

public abstract class RequestHandler<TRequest, TResult> 
    : IRequestHandler<TRequest, TResult>
    where TRequest : IRequest<TResult> {...}

public class GetUsersQueryHandler
    : RequestHandler<GetUsersQuery, IEnumerable<GetUsersResult>> {...}

Then I have a service factory were I register dependency injection like this:

public static void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IRequestHandler<GetUsersQuery, 
    IEnumerable<GetUsersResult>>, GetUsersQueryHandler>();
}

I can successfully resolve my handler like this:

var handler = 
_services.GetService<IRequestHandler<GetUsersQuery, IEnumerable<GetUsersResult>>>();

However, I would like to have a generic method in this factory that receives concrete implementation of IRequest and returns the appropriate handler without knowing the exact type beforehand, something like this:

public Task<TResult> Execute<TResult>(IRequest<TResult> request)
{
    var handler =
        _services.GetService<IRequestHandler<IRequest<TResult>, TResult>>();
    return handler.ExecuteAsync(request);
}

And call this method like this:

_serviceFactory.Execute(new GetUsersQuery(){});

Unfortunately this doesn't work, handler is not resolved and is null. I feel like this should be possible though.

Could you please tell me what I am doing wrong and how to achieve this?

Upvotes: 5

Views: 9081

Answers (2)

Steven
Steven

Reputation: 172606

This design might originate from this blog post. That same blog post shows a solution for your exact problem:

public TResult Process<TResult>(IQuery<TResult> query)
{
    var handlerType = typeof(IQueryHandler<,>)
        .MakeGenericType(query.GetType(), typeof(TResult));

    dynamic handler = container.GetInstance(handlerType);

    return handler.Handle((dynamic)query);
}

This translates, in your case, to the following:

public Task<TResult> Execute<TResult>(IRequest<TResult> request)
{
    var handlerType = typeof(IRequestHandler<,>)
        .MakeGenericType(request.GetType(), typeof(TResult));

    dynamic handler = _services.GetRequiredService(handlerType);

    return handler.ExecuteAsync((dynamic)query);
}

About the use of dynamic, the blog post states:

Unfortunately we need to call the Handle method using reflection (which is done, in this case, using the C# 4.0 dymamic keyword), because at this point it is impossible to cast the handler instance, since the generic TQuery argument is not available at compile time.

Upvotes: 5

Taras
Taras

Reputation: 235

I think I have found one way of doing this, execute method could be declared like this:

public Task<TResult> Execute<TRequest, TResult>(TRequest request)
        where TRequest : IRequest<TResult> 
{
    var handler = _services.GetService<IRequestHandler<TRequest, TResult>>();
    return handler.ExecuteAsync(request);
}

And used like this:

_serviceFactory.Execute<GetUsersQuery, IEnumerable<GetUsersResult>>(query);

This is a bit ugly, because I have to specify both request and result types for Execute method, would be better to just use _serviceFactory.Execute(query), but I suppose it might not be possible?

Upvotes: 0

Related Questions