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

时光怂恿深爱的人放手 提交于 2021-02-10 14:06:17

问题


I have a generic interface IPipelineBehavior<TRequest, TResponse> (from 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:

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

Result is a custom class I want all of my MediatR IRequestHandlers to return.

When the app runs, the service container gives me the following error:

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]]'

I can't see why because they clearly both have the same type arguments at runtime. 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>.

Does anyone understand why I'm getting this error, and what I need to do to solve it?


回答1:


What you wish to achieve is not supported in MS.DI. .NET Core's built-in DI Container is very limited (or perhaps even naive) when it comes to handling generic types.

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

IMO, your use case is very valid, which is why many DI Containers actually support this use case. My advise: pick a more mature DI Container and test whether it supports the way you want to apply generics.




回答2:


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. For anyone wondering, with Autofac the registration looks like this:

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

No further changes were required, except integrating Autofac with ASP.NET Core, of course.



来源:https://stackoverflow.com/questions/62064061/registering-generic-type-with-type-constraint-in-net-core-ms-di

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!