在 .NET Core 中注册具有类型约束的泛型类型 (MS.DI)

Registering generic type with type constraint in .NET Core (MS.DI)

我有一个通用接口 IPipelineBehavior<TRequest, TResponse>(来自 MediatR)。我正在尝试为此接口注册一个特定的行为,如下所示:

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

ValidationMiddleware 像这样实现 IPipelineBehavior

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

Result 是一个自定义 class 我希望我所有的 MediatR IRequestHandlers 到 return.

当应用程序运行时,服务容器给我以下错误:

System.ArgumentException: Implementation type 'StudentManagement.App.Middleware.ValidationMiddleware2[StudentManagement.App.Students.DisenrollStudentCommand,StudentManagement.App.Result1[System.Object]]' can't be converted to service type 'MediatR.IPipelineBehavior2[StudentManagement.App.Students.DisenrollStudentCommand,StudentManagement.App.Result1[System.Object]]'

我不明白为什么,因为它们在运行时显然都具有相同类型的参数。我的猜测是 Result<TResponse> 位由于某种原因导致了错误,因为其他行为在仅实现 IPipelineBehavior<TRequest, TResponse>.

时工作正常

有谁知道我为什么会收到这个错误,我需要做什么来解决它?

MS.DI 不支持您希望实现的目标。 .NET Core 的内置 DI 容器在处理泛型类型时非常有限(甚至可能是幼稚的)。

例如,以下是泛型类型的三个(相当基本的)用例,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 Container,测试它是否支持你想要的泛型应用方式。

正如 Steven 所建议的,事实证明内置的 .NET DI 容器在泛型方面非常有限。我最终切换到 Autofac,一切正常。对于任何想知道的人,使用 Autofac 的注册如下所示:

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

当然,除了将 Autofac 与 ASP.NET Core 集成外,无需进一步更改。