简体   繁体   English

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

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

I have a generic interface IPipelineBehavior<TRequest, TResponse> (from MediatR).我有一个通用接口IPipelineBehavior<TRequest, TResponse> (来自 MediatR)。 I'm trying to register a specific behavior for this interface as follows:我正在尝试为此接口注册一个特定的行为,如下所示:

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

ValidationMiddleware implements IPipelineBehavior like so: ValidationMiddleware像这样实现IPipelineBehavior

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

Result is a custom class I want all of my MediatR IRequestHandlers to return. Result是自定义 class 我希望我的所有 MediatR IRequestHandlers 返回。

When the app runs, the service container gives me the following error:当应用程序运行时,服务容器给我以下错误:

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.IPipelineBehavior 2[StudentManagement.App.Students.DisenrollStudentCommand,StudentManagement.App.Result 1[System.Object]]' 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 can't see why because they clearly both have the same type arguments at runtime.我不明白为什么,因为它们在运行时显然都具有相同的类型 arguments。 My guess is that the Result<TResponse> bit is causing the error for some reason, because other behaviors work fine when they just implement IPipelineBehavior<TRequest, TResponse> .我的猜测是Result<TResponse>位由于某种原因导致错误,因为其他行为在仅实现IPipelineBehavior<TRequest, TResponse>时工作正常。

Does anyone understand why I'm getting this error, and what I need to do to solve it?有谁知道我为什么会收到此错误,以及我需要做什么来解决它?

What you wish to achieve is not supported in MS.DI. MS.DI 不支持您希望实现的目标。 .NET Core's built-in DI Container is very limited (or perhaps even naive) when it comes to handling generic types. .NET Core 的内置 DI 容器在处理泛型类型时非常有限(甚至可能是幼稚的)。

For instance, here are three (rather basic) use cases for generic types, all not supported by MS.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>>();

Not only is this not supported, the exception messages thrown by MS.DI will be very unhelpful and confusing.这不仅不受支持,而且 MS.DI 抛出的异常消息将非常无用且令人困惑。

IMO, your use case is very valid, which is why many DI Containers actually support this use case. IMO,你的用例非常有效,这就是为什么许多 DI 容器实际上支持这个用例。 My advise: pick a more mature DI Container and test whether it supports the way you want to apply generics.我的建议:选择一个比较成熟的DI Container,测试它是否支持你想申请的方式 generics。

As Steven suggested, it turns out the built-in .NET DI container is pretty limited when it comes to generics. I ended up switching to Autofac and everything worked fine.正如史蒂文所建议的那样,事实证明内置的 .NET DI 容器在涉及 generics 时非常有限。我最终切换到 Autofac,一切正常。 For anyone wondering, with Autofac the registration looks like this:对于任何想知道的人,使用 Autofac 的注册看起来像这样:

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

No further changes were required, except integrating Autofac with ASP.NET Core, of course.当然,除了将 Autofac 与 ASP.NET Core 集成之外,不需要进一步的更改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM