Внедрение зависимостей .NET Core, разрешение универсального интерфейса - PullRequest
0 голосов
/ 09 ноября 2018

У меня проблемы с внедрением зависимости ядра asp.net, я не могу разрешить универсальный интерфейс из IServiceProvider. Вот мои настройки:

Общие интерфейсы:

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

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

Конкретная реализация:

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>> {...}

Тогда у меня есть фабрика сервисов, где я регистрирую внедрение зависимости следующим образом:

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

Я могу успешно разрешить свой обработчик следующим образом:

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

Однако я хотел бы иметь универсальный метод в этой фабрике, который получает конкретную реализацию IRequest и возвращает соответствующий обработчик, не зная заранее точного типа, что-то вроде этого:

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

И вызовите этот метод так:

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

К сожалению, это не работает, обработчик не разрешен и имеет значение null. Я чувствую, что это должно быть возможно, хотя.

Не могли бы вы сказать, что я делаю неправильно и как этого добиться?

Ответы [ 2 ]

0 голосов
/ 11 ноября 2018

Этот дизайн может происходить из этого сообщения в блоге. В той же записи блога показано решение вашей конкретной проблемы:

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

Это переводит, в вашем случае, к следующему:

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

Об использовании dynamic в блоге говорится:

К сожалению, нам нужно вызывать метод Handle с помощью отражения (используя в этом случае ключевое слово dymamic C # 4.0), потому что на данном этапе невозможно привести экземпляр обработчика, так как универсальный аргумент TQuery недоступен во время компиляции. .

0 голосов
/ 09 ноября 2018

Я думаю, что нашел один способ сделать это, метод execute мог бы быть объявлен так:

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

И используется так:

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

Это немного уродливо, потому что для метода Execute я должен указать и тип запроса, и результаты. Было бы лучше просто использовать _serviceFactory.Execute (query), но я полагаю, что это может быть невозможно?

...