简体   繁体   English

在解析实现相同接口的泛型类型时,Autofac问题需要Resolve()两次或更多次

[英]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>响应类型的请求,其中GameTrophy实现了上述两个多租户接口中的一个。

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>responseHandlersMultiTenantHandler类的实例将被注入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>responseHandlersMultiTenantOptionalHandler类的实例将被注入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, IEnumerable 1 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,IEnumerable 1 p)\\r\\n at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable 1参数)\\ r \\ n在Autofac.Core.Resolving.Instan 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 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(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 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,IEnumerable 1 parameters)\\r\\n at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable Resof.InstanceLookup.Activate 1 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注册,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.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(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 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(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在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__16 2.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__16 2.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, IEnumerable 1 p)\\r\\n at Autofac.Core.Activators.Delegate.DelegateActivator.ActivateInstance(IComponentContext context, IEnumerable 1 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, 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(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(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在Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate()\\ r \\ n在Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable 1 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, 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(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 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(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在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 . 但是,如果此检查为falseParameterCompatibleWithTypeConstraint不返回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> )和参数的泛型参数( GameTrophy )创建泛型类型。

Since IQueryable<T> obviously does not define the IMultitenant or IMultitenantOptional type constraints this is always possible and the method returns true . 由于IQueryable<T>显然没有定义IMultitenantIMultitenantOptional类型约束,因此这总是可行的,并且该方法返回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开发人员正在考虑这个问题。

The question remains why it is working the second time you try to resolve the service. 问题仍然是为什么它第二次尝试解析服务时工作。

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.

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