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

时间:2016-09-02 11:16:24

标签: c# generics autofac

我有一个与Autofac相关的问题,它解决了实现相同接口但具有不同类型约束的泛型类型。也可能是我实现这个特定用例(进一步描述)的方法是完全错误的,在这种情况下我会感谢有人纠正我的想法。

有关控制台应用示例的信息,请参阅https://gist.github.com/sebekz/4c658a5c7551ba5a2b3fd81488ea3ee7,有关其他详细信息,请参阅下文。

我所拥有的是两个接口

public interface IMultitenant
public interface IMultitenantOptional

这些接口由一堆类实现,这些类是IQueryable<T>通用类型的响应。所以我可以得到具有例如响应类型的请求。 IQueryable<Game>IQueryable<Trophy>,其中GameTrophy实现上述两个多租户接口之一。

现在,我有两个非常相似的类定义

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>

这些类的实例由Autofac在一个单独的类中构造注入:

public class MediatorPipeline<TRequest, TResponse> : RequestHandler<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    public MediatorPipeline(
        IResponseHandler<TRequest, TResponse>[] responseHandlers
    )
}   

Autofac配置如下:

builder.RegisterGeneric(typeof(MultiTenantHandler<,>))
    .AsImplementedInterfaces()
    .SingleInstance();   
builder.RegisterGeneric(typeof(MultiTenantOptionalHandler<,>))
    .AsImplementedInterfaces()
    .SingleInstance();

我期望当MediatorPipeline类拦截返回类型为IQueryable<IMultitenant>的响应时,MultiTenantHandler类的实例将被注入responseHandlers变量。类似地,当MediatorPipeline类截获返回类型为IQueryable<IMultitenantOptional>的响应时,MultiTenantOptionalHandler类的实例将被注入responseHandlers变量。

如果这些注入类属于这两个特定的IQueryable子类型,则会对我的响应进行后处理。

这一切都是建立和运作的。的种类。它是WebAPI项目的一部分,问题是,当我运行我的第一次和第二次返回IQueryable<IMultitenant>的端点时,我得到:

  

&#34; message&#34;:&#34;执行解析时抛出异常   操作。有关详细信息,请参阅InnerException。 ---&GT;   GenericArguments [1],&#39; System.Linq.IQueryable&#39; 1 [Game]&#39;,on   &#39; MultiTenantOptionalHandler&#39; 2 [TRequest,τ响应]&#39;违反了   类型约束&#39; TResponse&#39;。 (详见内部异常。)&#34;,           &#34;输入&#34;:&#34; Autofac.Core.DependencyResolutionException&#34;,

首次执行在堆栈跟踪中有更深层次的内容:

  

&#34; stacktrace&#34;:&#34;在   System.RuntimeType.ValidateGenericArguments(MemberInfo定义,   RuntimeType [] genericArguments,Exception e)\ r \ n at   System.RuntimeType.MakeGenericType(Type [] instantiation)\ r \ n at   Autofac.Features.OpenGenerics.OpenGenericServiceBinder.TryBindServiceType(服务   服务,IEnumerable 1 configuredOpenGenericServices, Type openGenericImplementationType, Type& constructedImplementationType, IEnumerable 1&amp; constructServices)\ r \ n at   Autofac.Features.OpenGenerics.OpenGenericRegistrationSource.d__0.MoveNext(个)\ r \ n   在   Autofac.Core.Registration.ComponentRegistry.GetInitializedServiceInfo(服务   服务)\ r \ n at   Autofac.Core.Registration.ComponentRegistry.RegistrationsFor(服务   服务)\ r \ n at   Autofac.Features.Collections.CollectionRegistrationSource&LT;&GT; 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 at   Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate(个)\ r \ n   在   Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext   上下文,IEnumerable 1 parameters)\r\n at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable 1   参数)\ r \ n at   Autofac.Core.Resolving.InstanceLookup.Execute()\ r \ n at   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个参数)\ 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 at   Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration   注册,IEnumerable`1参数)&#34;,

而第二次执行在同一个地方:

  

&#34; stacktrace&#34;:&#34;在   System.RuntimeType.ValidateGenericArguments(MemberInfo定义,   RuntimeType [] genericArguments,Exception e)\ r \ n at   System.RuntimeType.MakeGenericType(Type [] instantiation)\ r \ n at   Autofac.Features.OpenGenerics.OpenGenericServiceBinder.TryBindServiceType(服务   服务,IEnumerable 1 configuredOpenGenericServices, Type openGenericImplementationType, Type& constructedImplementationType, IEnumerable 1&amp; constructServices)\ r \ n at   Autofac.Features.OpenGenerics.OpenGenericRegistrationSource.d__0.MoveNext(个)\ r \ n   在   Autofac.Core.Registration.ComponentRegistry.GetInitializedServiceInfo(服务   服务)\ r \ n at   Autofac.Core.Registration.ComponentRegistry.RegistrationsFor(服务   服务)\ r \ n at   System.Linq.Enumerable.d__16 2.MoveNext()\r\n at System.Linq.Enumerable.WhereSelectEnumerableIterator 2.MoveNext(个)\ r \ n   在   Autofac.Core.Registration.ComponentRegistry.GetInitializedServiceInfo(服务   服务)\ r \ n at   Autofac.Core.Registration.ComponentRegistry.RegistrationsFor(服务   服务)\ r \ n at   Autofac.Features.Collections.CollectionRegistrationSource&LT;&GT; 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 at   Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate(个)\ r \ n   在   Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext   上下文,IEnumerable 1 parameters)\r\n at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable 1   参数)\ r \ n at   Autofac.Core.Resolving.InstanceLookup.Execute()\ r \ n at   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个参数)\ 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 at   Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration   注册,IEnumerable`1参数)&#34;,

第三次和所有后续执行都没有任何问题,返回预期的有效负载。

我很感激Autofac专家的一些帮助。

修改

我创建了一个控制台程序来说明问题:

https://gist.github.com/sebekz/4c658a5c7551ba5a2b3fd81488ea3ee7

编辑2:

最终向Autofac存储库发出拉取请求,该存储库目前已合并到开发中,可能会被包含在下一个Autofac版本中。请在此处跟踪整合进度https://github.com/autofac/Autofac/pull/796

1 个答案:

答案 0 :(得分:1)

正如您在堆栈跟踪中看到的那样,异常是由TryBindService类的OpenGenericServiceBinder方法引起的。此方法尝试查找给定类型参数的兼容服务。它调用IsCompatibleWithGenericParameterConstraints方法以确保类型参数与给定的类型约束兼容,因此我怀疑这种方法是错误的,或者至少不能按预期工作。

这种怀疑被证明是正确的,因为该方法会为以下所有调用返回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

那为什么会返回true?它使用方法ParameterCompatibleWithTypeConstraint来检查参数是否与给定的类型约束兼容。首先,此方法检查约束类型是否可从参数类型分配,因此:

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

到目前为止,它正在按预期工作。但是,如果此检查为false,则ParameterCompatibleWithTypeConstraint不会返回false。相反,它会检查它是否可以使用约束的基本类型(IQueryable<T>)和参数的通用参数(GameTrophy)创建泛型类型。

由于IQueryable<T>显然没有定义IMultitenantIMultitenantOptional类型约束,因此始终可以使用此方法返回true

我不确定为什么要进行第二次检查。对我来说这看起来像个错误,但可能有一个正当理由,我现在没有看到。我想你最好的选择是在GitHub上创建一个问题,看看Autofac开发人员在考虑这个问题。

问题仍然是您第二次尝试解析服务时的工作原理。

我还没有确认,但我怀疑它可能与缓存有关。也许Autofac记得使用MultiTenantOptionalHandler无法解析IResponseHandler<IRequest<IQueryable<Game>>, IQueryable<Game>>,因此它使用了唯一的其他可能的注册,这是正确的注册(MultiTenantHandler)巧合。

这个假设得到以下事实的支持:我必须解决IResponseHandler<IRequest<IQueryable<Game>>, IQueryable<Game>> 三次次以获得成功,以防我添加另一个IResponseHandler实现,但还有其他类型限制。

  

也可能是我实现这个特定用例(进一步描述)的方法是完全错误的,在这种情况下,我会感谢有人纠正我的想法。

我不知道你的方法是否是最佳做法,但我没有看到任何错误,我认为如果ParameterCompatibleWithTypeConstraint方法按预期工作,它应该可以正常工作。