在 .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.Result
1[System.Object]]' can't be converted to service type 'MediatR.IPipelineBehavior2[StudentManagement.App.Students.DisenrollStudentCommand,StudentManagement.App.Result
1[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 集成外,无需进一步更改。
我有一个通用接口 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.ValidationMiddleware
2[StudentManagement.App.Students.DisenrollStudentCommand,StudentManagement.App.Result
1[System.Object]]' can't be converted to service type 'MediatR.IPipelineBehavior2[StudentManagement.App.Students.DisenrollStudentCommand,StudentManagement.App.Result
1[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 集成外,无需进一步更改。