我有一个与Autofac相关的问题,它解决了实现相同接口但具有不同类型约束的泛型类型。也可能是我实现这个特定用例(进一步描述)的方法是完全错误的,在这种情况下我会感谢有人纠正我的想法。
有关控制台应用示例的信息,请参阅https://gist.github.com/sebekz/4c658a5c7551ba5a2b3fd81488ea3ee7,有关其他详细信息,请参阅下文。
我所拥有的是两个接口
public interface IMultitenant
public interface IMultitenantOptional
这些接口由一堆类实现,这些类是IQueryable<T>
通用类型的响应。所以我可以得到具有例如响应类型的请求。 IQueryable<Game>
或IQueryable<Trophy>
,其中Game
和Trophy
实现上述两个多租户接口之一。
现在,我有两个非常相似的类定义
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,IEnumerable1 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 at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate(个)\ r \ n 在 Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext 上下文,IEnumerable1 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注册, IEnumerable1 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 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__162.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,IEnumerable1 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 at Autofac.Core.Activators.Reflection.ConstructorParameterBinding.Instantiate(个)\ r \ n 在 Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext 上下文,IEnumerable1 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注册, IEnumerable1 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 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。
答案 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>
)和参数的通用参数(Game
或Trophy
)创建泛型类型。
由于IQueryable<T>
显然没有定义IMultitenant
或IMultitenantOptional
类型约束,因此始终可以使用此方法返回true
。
我不确定为什么要进行第二次检查。对我来说这看起来像个错误,但可能有一个正当理由,我现在没有看到。我想你最好的选择是在GitHub上创建一个问题,看看Autofac开发人员在考虑这个问题。
我还没有确认,但我怀疑它可能与缓存有关。也许Autofac记得使用MultiTenantOptionalHandler
无法解析IResponseHandler<IRequest<IQueryable<Game>>, IQueryable<Game>>
,因此它使用了唯一的其他可能的注册,这是正确的注册(MultiTenantHandler
)巧合。
这个假设得到以下事实的支持:我必须解决IResponseHandler<IRequest<IQueryable<Game>>, IQueryable<Game>>
三次次以获得成功,以防我添加另一个IResponseHandler
实现,但还有其他类型限制。
也可能是我实现这个特定用例(进一步描述)的方法是完全错误的,在这种情况下,我会感谢有人纠正我的想法。
我不知道你的方法是否是最佳做法,但我没有看到任何错误,我认为如果ParameterCompatibleWithTypeConstraint
方法按预期工作,它应该可以正常工作。