Регистрация обобщенного типа c с ограничением типа в. NET Core (MS.DI) - PullRequest
1 голос
/ 28 мая 2020

У меня общий c интерфейс IPipelineBehavior<TRequest, TResponse> (от MediatR). Я пытаюсь зарегистрировать определенное c поведение для этого интерфейса следующим образом:

services.AddTransient(typeof(IPipelineBehavior<,>),
    typeof(ValidationMiddleware<,>));

ValidationMiddleware реализует IPipelineBehavior вот так:

public class ValidationMiddleware<TRequest, TResponse>
    : IPipelineBehavior<TRequest, Result<TResponse>>

Result is настраиваемый класс, который я хочу, чтобы все мои обработчики MediatR IRequestHandler возвращали.

При запуске приложения контейнер службы выдает следующую ошибку:

System.ArgumentException: тип реализации 'StudentManagement .App.Middleware.ValidationMiddleware 2[StudentManagement.App.Students.DisenrollStudentCommand,StudentManagement.App.Result 1 [System.Object]] 'не может быть преобразовано в тип службы' MediatR.IPipelineBehavior 2[StudentManagement.App.Students.DisenrollStudentCommand,StudentManagement.App.Result 1 [System.Object]] '

I не могу понять почему, потому что они оба явно имеют аргументы одного и того же типа во время выполнения. Я предполагаю, что бит Result<TResponse> по какой-то причине вызывает ошибку, потому что другие варианты поведения работают нормально, когда они просто реализуют IPipelineBehavior<TRequest, TResponse>.

Кто-нибудь понимает, почему я получаю эту ошибку и что я что нужно сделать, чтобы решить эту проблему?

Ответы [ 2 ]

2 голосов
/ 28 мая 2020

То, что вам нужно sh, не поддерживается в MS.DI. . NET Встроенный в ядро ​​контейнер DI очень ограничен (или, возможно, даже наивен), когда дело доходит до обработки общих c типов.

Например, вот три (скорее базовые c) использования случаи для общих типов c, все не поддерживаемые MS.DI:

// Example generic abstraction
public interface IGeneric<TKey, TValue> where TKey : struct { }

// Type with a different number of generic types than the abstraction
public class MonoGeneric<TKey> : IGeneric<TKey, string> where TKey : struct { }

// Type with the generic types in a different order
public class SwappedGeneric<TValue, TKey> : IGeneric<TKey, TValue>
    where TKey : struct { }

// Type where the generic types don't exactly map to those of the abstraction
// NOTE: This is your use case.
public class ConstrainedGeneric<TKey, TValue> : IGeneric<TKey, List<TValue>>
    where TKey : struct { }

// Registration
services.AddTransient(typeof(IGeneric<,>), typeof(MonoGeneric<>));
services.AddTransient(typeof(IGeneric<,>), typeof(SwappedGeneric<,>));
services.AddTransient(typeof(IGeneric<,>), typeof(ConstrainedGeneric<,>));

// Usage
// Should work on any of the registrations, but it fails on all!
provider.GetRequiredService<IGeneric<int, string>>();

Мало того, что это не поддерживается, сообщения об исключениях, создаваемые MS.DI, будут очень бесполезными и запутанными.

IMO, ваш вариант использования очень допустим, поэтому многие контейнеры DI фактически поддерживают этот вариант использования. Мой совет: выберите более зрелый контейнер DI и проверьте, поддерживает ли он тот способ, которым вы хотите применять дженерики.

0 голосов
/ 28 мая 2020

Как предположил Стивен, оказывается, что встроенный. NET контейнер DI довольно ограничен, когда дело касается дженериков. В итоге я переключился на Autofa c, и все работало нормально. Для всех, кому интересно, с Autofa c регистрация выглядит так:

builder.RegisterGeneric(typeof(ValidationMiddleware<,>))
    .As(typeof(IPipelineBehavior<,>))
    .InstancePerDependency();

Никаких дополнительных изменений не потребовалось, кроме интеграции Autofa c с ASP. NET Core, конечно.

...