[英]Autofac issues when resolving generic types implementing the same interface, need to Resolve() twice or more
I have an Autofac related issue with resolving generic types which implement the same interface, but have different type constraints. 我有一个与Autofac相关的问题,解决了实现相同接口但具有不同类型约束的泛型类型。 It could also be that my approach to implementing this particular use case (described further down) is plain wrong, in such case I would appreciate someone to correct my thinking.
也可能是我实现这个特定用例(进一步描述)的方法是完全错误的,在这种情况下,我希望有人能够纠正我的想法。
See https://gist.github.com/sebekz/4c658a5c7551ba5a2b3fd81488ea3ee7 for a console app sample and read below for additional details. 有关控制台应用示例,请参阅https://gist.github.com/sebekz/4c658a5c7551ba5a2b3fd81488ea3ee7 ,有关其他详细信息,请参阅下文。
What I have is two interfaces 我有两个接口
public interface IMultitenant
public interface IMultitenantOptional
These interfaces are implemented by a bunch of classes, which are IQueryable<T>
generic types of responses. 这些接口由一堆类实现,这些类是
IQueryable<T>
泛型类型的响应。 So I could have requests which have response types of eg IQueryable<Game>
or IQueryable<Trophy>
, where both Game
and Trophy
implement one of two above mentioned multitenancy interfaces. 所以我可以拥有具有例如
IQueryable<Game>
或IQueryable<Trophy>
响应类型的请求,其中Game
和Trophy
实现了上述两个多租户接口中的一个。
Now, I have two very similar class definitions 现在,我有两个非常相似的类定义
public class MultiTenantHandler<TRequest, TResponse> : IResponseHandler<TRequest, TResponse>
where TResponse : IQueryable<IMultitenant>
public class MultiTenantOptionalHandler<TRequest, TResponse> : IResponseHandler<TRequest, TResponse>
where TResponse : IQueryable<IMultitenantOptional>
public interface IResponseHandler<in TRequest, TResponse>
Instances of these classes are constructor-injected by Autofac in a separate class: 这些类的实例由Autofac在一个单独的类中构造注入:
public class MediatorPipeline<TRequest, TResponse> : RequestHandler<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
public MediatorPipeline(
IResponseHandler<TRequest, TResponse>[] responseHandlers
)
}
Autofac configuration goes like this: Autofac配置如下:
builder.RegisterGeneric(typeof(MultiTenantHandler<,>))
.AsImplementedInterfaces()
.SingleInstance();
builder.RegisterGeneric(typeof(MultiTenantOptionalHandler<,>))
.AsImplementedInterfaces()
.SingleInstance();
What I expected is that when the MediatorPipeline
class intercepted a response with a return type of IQueryable<IMultitenant>
, an instance of MultiTenantHandler
class will be injected into the responseHandlers
variable. 我期望的是当
MediatorPipeline
类截获返回类型为IQueryable<IMultitenant>
的responseHandlers
, MultiTenantHandler
类的实例将被注入responseHandlers
变量。 Similarily, when the MediatorPipeline
class intercepted a response with a return type of IQueryable<IMultitenantOptional>
, an instance of MultiTenantOptionalHandler
class will be injected into the responseHandlers
variable. 类似地,当
MediatorPipeline
类截获返回类型为IQueryable<IMultitenantOptional>
的responseHandlers
, MultiTenantOptionalHandler
类的实例将被注入responseHandlers
变量。
These injected classes post-process my responses if they are of these two specific IQueryable
sub-types. 如果它们属于这两个特定的
IQueryable
子类型,那么这些注入的类会对我的响应进行后处理。
It all builds and works. 这一切都建立和工作。 Kind of.
的种类。 It's all a part of a WebAPI project and the problem is, when I run my endpoint which is supposed to return
IQueryable<IMultitenant>
for the first and second time, I get: 它都是WebAPI项目的一部分,问题是,当我运行我的端点时,它应该首次返回
IQueryable<IMultitenant>
,我得到:
"message": "An exception was thrown while executing a resolve operation. See the InnerException for details. ---> GenericArguments[1], 'System.Linq.IQueryable'1[Game]', on 'MultiTenantOptionalHandler'2[TRequest,TResponse]' violates the constraint of type 'TResponse'. (See inner exception for details.)", "type": "Autofac.Core.DependencyResolutionException",
“message”:“执行解析操作时抛出异常。有关详细信息,请参阅InnerException .--- GenericArguments [1],'System.Linq.IQueryable'1 [Game]','MultiTenantOptionalHandler'2 [TRequest ,TResponse]'违反了'TResponse'类型的约束。(详见内部异常。)“,”type“:”Autofac.Core.DependencyResolutionException“,
First execution has this deeper in the stack trace: 第一次执行在堆栈跟踪中有更深层次:
"stacktrace": " at System.RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e)\\r\\n at System.RuntimeType.MakeGenericType(Type[] instantiation)\\r\\n at Autofac.Features.OpenGenerics.OpenGenericServiceBinder.TryBindServiceType(Service service, IEnumerable
1 configuredOpenGenericServices, Type openGenericImplementationType, Type& constructedImplementationType, IEnumerable
1& constructedServices)\\r\\n at Autofac.Features.OpenGenerics.OpenGenericRegistrationSource.d__0.MoveNext()\\r\\n at Autofac.Core.Registration.ComponentRegistry.GetInitializedServiceInfo(Service service)\\r\\n at Autofac.Core.Registration.ComponentRegistry.RegistrationsFor(Service service)\\r\\n at Autofac.Features.Collections.CollectionRegistrationSource.<>c__DisplayClass4.b__0(IComponentContext c, IEnumerable1 p)\\r\\n at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable
1 parameters)\\r\\n at Autofac.Core.Resolving.Instan“stacktrace”:“在System.RuntimeType.ValidateGenericArguments(MemberInfo定义,RuntimeType [] genericArguments,Exception e)\\ r \\ n在System.RuntimeType.MakeGenericType(Type [] instantiation)\\ r \\ n在Autofac.Features.OpenGenerics中。 OpenGenericServiceBinder.TryBindServiceType(服务服务,IEnumerable
1 configuredOpenGenericServices, Type openGenericImplementationType, Type& constructedImplementationType, IEnumerable
1&constructServices)\\ r \\ n在Autofac.Features.OpenGenerics.OpenGenericRegistrationSource.d__0.MoveNext()\\ r \\ n在Autofac.Core.Registration处。 ComponentRegistry.GetInitializedServiceInfo(服务服务)\\ r \\ n at Autofac.Core.Registration.ComponentRegistrationsFor(服务服务)\\ r \\ n在Autofac.Features.Collections.CollectionRegistrationSource。<> c__DisplayClass4.b__0(IComponentContext c,IEnumerable1 p)\\r\\n at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable
1参数)\\ r \\ n在Autofac.Core.Resolving.Instan ceLookup.Activate(IEnumerable1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Execute()\\r\\n at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable
1 parameters)\\r\\n at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()\\r\\n at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable
1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Execute()\\r\\n at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable1 parameters)\\r\\n at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()\\r\\n at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable
ceLookup.Activate(IEnumerable
1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Execute()\\r\\n at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable
1参数) r \\ n at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()\\ r \\ n at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context,IEnumerable1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable
Resof.InstanceLookup.Activate1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable
1参数)\\ r \\ n在Autofac.Core.Resolving.InstanceLookup.Execute()\\ r \\ n在Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope,IComponentRegistration注册,IEnumerable1 parameters)\\r\\n at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()\\r\\n at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable
1 parameters)\\r\\n at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()\\r\\n at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable
1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Execute()\\r\\n at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable
1 parameters)\\r\\n at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)",1 parameters)\\r\\n at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()\\r\\n at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable
1参数)\\ r \\ n at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Execute()\\r\\n at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable
1参数)\\ r \\ n在Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration,IEnumerable`1 parameters)“,
while the second execution has this in the same place: 而第二次执行在同一个地方:
"stacktrace": " at System.RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e)\\r\\n at System.RuntimeType.MakeGenericType(Type[] instantiation)\\r\\n at Autofac.Features.OpenGenerics.OpenGenericServiceBinder.TryBindServiceType(Service service, IEnumerable
1 configuredOpenGenericServices, Type openGenericImplementationType, Type& constructedImplementationType, IEnumerable
1& constructedServices)\\r\\n at Autofac.Features.OpenGenerics.OpenGenericRegistrationSource.d__0.MoveNext()\\r\\n at Autofac.Core.Registration.ComponentRegistry.GetInitializedServiceInfo(Service service)\\r\\n at Autofac.Core.Registration.ComponentRegistry.RegistrationsFor(Service service)\\r\\n at System.Linq.Enumerable.d__162.MoveNext()\\r\\n at System.Linq.Enumerable.WhereSelectEnumerableIterator
2.MoveNext()\\r\\n at Autofac.Core.Registration.ComponentRegistry.GetInitializedServiceInfo(Service service)\\r\\n at Autofac.Core.Registration.ComponentRegistry.RegistrationsFor(Service“stacktrace”:“在System.RuntimeType.ValidateGenericArguments(MemberInfo定义,RuntimeType [] genericArguments,Exception e)\\ r \\ n在System.RuntimeType.MakeGenericType(Type [] instantiation)\\ r \\ n在Autofac.Features.OpenGenerics中。 OpenGenericServiceBinder.TryBindServiceType(服务服务,IEnumerable
1 configuredOpenGenericServices, Type openGenericImplementationType, Type& constructedImplementationType, IEnumerable
1&constructServices)\\ r \\ n在Autofac.Features.OpenGenerics.OpenGenericRegistrationSource.d__0.MoveNext()\\ r \\ n在Autofac.Core.Registration处。 ComponentRegistry.GetInitializedServiceInfo(服务服务)\\ r \\ n在Autofac.Core.Registration.ComponentRegistry.RegistrationsFor(服务服务)\\ r \\ n在System.Linq.Enumerable.d__162.MoveNext()\\r\\n at System.Linq.Enumerable.WhereSelectEnumerableIterator
2.MoveNext()\\ r \\ n at Autofac.Core.Registration.ComponentRegistry.GetInitializedServiceInfo(Service service)\\ r \\ n at Autofac.Core.Registration.ComponentRegistry.RegistrationsFor(Service service)\\r\\n at Autofac.Features.Collections.CollectionRegistrationSource.<>c__DisplayClass4.b__0(IComponentContext c, IEnumerable1 p)\\r\\n at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable
1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Execute()\\r\\n at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable
1 parameters)\\r\\n at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()\\r\\n at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable
1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Execute()\\r\\n at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharin服务)\\ r \\ n在Autofac.Features.Collections.CollectionRegistrationSource。<> c__DisplayClass4.b__0(IComponentContext c,IEnumerable
1 p)\\r\\n at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable
1)参数)\\ r \\ n at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Execute()\\r\\n at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable
1参数)\\ r \\ n在Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()\\ r \\ n在Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable
1参数)\\ r \\ n在Autofac.Core.Resolving.InstanceLookup.Execute()\\ r \\ n在Autofac.Core.Resolving。 ResolveOperation.GetOrCreateInstance(ISharin gLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable1 parameters)\\r\\n at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()\\r\\n at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable
1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Execute()\\r\\n at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable
1 parameters)\\r\\n at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)",gLifetimeScope currentOperationScope,IComponentRegistration注册,IEnumerable
1 parameters)\\r\\n at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()\\r\\n at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable
1)参数)\\ r \\ n at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Execute()\\r\\n at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable
1参数)\\ r \\ n在Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration,IEnumerable`1 parameters)“,
Third and all subsequent executions pass without any issues, returning expected payloads. 第三次和所有后续执行都没有任何问题,返回预期的有效负载。
I would appreciate some help from Autofac experts. 我很感激Autofac专家提供的一些帮助。
EDIT: 编辑:
I created a console program which illustrates the issue: 我创建了一个控制台程序来说明问题:
https://gist.github.com/sebekz/4c658a5c7551ba5a2b3fd81488ea3ee7 https://gist.github.com/sebekz/4c658a5c7551ba5a2b3fd81488ea3ee7
EDIT 2: 编辑2:
This ended up with a pull request into the Autofac repository which so far has been merged into develop with a potential for being included in one of the next Autofac releases. 这最终得到了一个进入Autofac存储库的拉取请求,该存储库到目前为止已合并到开发中,有可能被包含在下一个Autofac版本中。 Please track the integration progress here https://github.com/autofac/Autofac/pull/796 .
请在此处跟踪集成进度https://github.com/autofac/Autofac/pull/796 。
As you can see in the stacktrace the exception is caused in the TryBindService
method from the OpenGenericServiceBinder
class. 正如您在stacktrace中看到的那样,异常是由
OpenGenericServiceBinder
类中的TryBindService
方法引起的。 This method tries to find a compatible service for the given type parameters. 此方法尝试查找给定类型参数的兼容服务。 It calls the
IsCompatibleWithGenericParameterConstraints
method to ensure that the type parameters are compatible with the given type constraints, so I suspected that this method is wrong or at least not working as expected. 它调用
IsCompatibleWithGenericParameterConstraints
方法来确保类型参数与给定的类型约束兼容,因此我怀疑这种方法是错误的,或者至少不能按预期工作。
This suspicion proved to be correct, since the method returns true
for all of the following calls: 这种怀疑被证明是正确的,因为该方法对以下所有调用都返回
true
:
typeof(MultiTenantHandler<,>).IsCompatibleWithGenericParameterConstraints(new[] { typeof(IQueryable<Game>), typeof(IQueryable<Game>) }); // expected: true
typeof(MultiTenantHandler<,>).IsCompatibleWithGenericParameterConstraints(new[] { typeof(IQueryable<Trophy>), typeof(IQueryable<Trophy>) }); // expected: false
typeof(MultiTenantOptionalHandler<,>).IsCompatibleWithGenericParameterConstraints(new[] { typeof(IQueryable<Game>), typeof(IQueryable<Game>) }); // expected: false
typeof(MultiTenantOptionalHandler<,>).IsCompatibleWithGenericParameterConstraints(new[] { typeof(IQueryable<Trophy>), typeof(IQueryable<Trophy>) }); // expected: true
So why does it return true
? 那么,为什么它返回
true
? It uses the method ParameterCompatibleWithTypeConstraint
to check whether the parameter is compatible with the given type constraint. 它使用方法
ParameterCompatibleWithTypeConstraint
来检查参数是否与给定的类型约束兼容。 At first this method checks if the constraint type is assignable from the parameter type, so: 首先,此方法检查约束类型是否可从参数类型分配,因此:
typeof(IQueryable<IMultitenant>).IsAssignableFrom(typeof(IQueryable<Game>)); // true
typeof(IQueryable<IMultitenant>).IsAssignableFrom(typeof(IQueryable<Trophy>)); // false
typeof(IQueryable<IMultitenantOptional>).IsAssignableFrom(typeof(IQueryable<Game>)); // false
typeof(IQueryable<IMultitenantOptional>).IsAssignableFrom(typeof(IQueryable<Trophy>)); // true
So far it is working as expected. 到目前为止它正在按预期工作。 However, in case this check is
false
the ParameterCompatibleWithTypeConstraint
does not return false
. 但是,如果此检查为
false
则ParameterCompatibleWithTypeConstraint
不返回false
。 Instead it checks if it can create a generic type with the base type of the constraint ( IQueryable<T>
) and the generic argument of the parameter ( Game
or Trophy
). 相反,它会检查它是否可以使用约束的基本类型(
IQueryable<T>
)和参数的泛型参数( Game
或Trophy
)创建泛型类型。
Since IQueryable<T>
obviously does not define the IMultitenant
or IMultitenantOptional
type constraints this is always possible and the method returns true
. 由于
IQueryable<T>
显然没有定义IMultitenant
或IMultitenantOptional
类型约束,因此这总是可行的,并且该方法返回true
。
I am not sure why this second check is performed. 我不确定为什么要进行第二次检查。 It looks like a bug to me, but there may be a valid reason for this I am not seeing right now.
对我来说这看起来像个错误,但可能有一个正当理由,我现在没有看到。 I guess your best bet would be to create an issue on GitHub to see what the Autofac developers are thinking about this.
我想你最好的选择是在GitHub上创建一个问题,看看Autofac开发人员正在考虑这个问题。
I have not confirmed it, but I suspect it may have to do with caching. 我还没有确认,但我怀疑它可能与缓存有关。 Maybe Autofac remembers that using the
MultiTenantOptionalHandler
did not work to to resolve an IResponseHandler<IRequest<IQueryable<Game>>, IQueryable<Game>>
, so it uses the only other possible registration, which is the correct one ( MultiTenantHandler
) by coincidence. 也许Autofac记得使用
MultiTenantOptionalHandler
无法解析IResponseHandler<IRequest<IQueryable<Game>>, IQueryable<Game>>
,所以它使用唯一的其他可能的注册,这是正确的( MultiTenantHandler
)巧合。
This assumption is backed by the fact that I have to resolve the IResponseHandler<IRequest<IQueryable<Game>>, IQueryable<Game>>
three times to be successful in case I add another IResponseHandler
implementation with yet another type constraints. 这个假设是事实,我必须解决备份
IResponseHandler<IRequest<IQueryable<Game>>, IQueryable<Game>>
三次成功的情况下,我再添IResponseHandler
实现又一类型的限制。
It could also be that my approach to implementing this particular use case (described further down) is plain wrong, in such case I would appreciate someone to correct my thinking.
也可能是我实现这个特定用例(进一步描述)的方法是完全错误的,在这种情况下,我希望有人能够纠正我的想法。
I don't know if your approach is best practice, but I don't see anything wrong about it and I guess it should work fine if the ParameterCompatibleWithTypeConstraint
method would work as expected. 我不知道你的方法是否是最佳实践,但我没有看到任何错误,我认为如果
ParameterCompatibleWithTypeConstraint
方法按预期工作,它应该可以正常工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.