gpt4 book ai didi

c# - 解决实现相同接口(interface)的泛型类型时的 Autofac 问题,需要 Resolve() 两次或更多次

转载 作者:行者123 更新时间:2023-11-30 17:36:32 28 4
gpt4 key购买 nike

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

参见 https://gist.github.com/sebekz/4c658a5c7551ba5a2b3fd81488ea3ee7有关控制台应用程序示例,请阅读下文以了解更多详细信息。

我有两个接口(interface)

public interface IMultitenant
public interface IMultitenantOptional

这些接口(interface)是由一堆类实现的,分别是IQueryable<T>通用类型的响应。所以我可以有响应类型的请求,例如IQueryable<Game>IQueryable<Trophy> ,其中两个GameTrophy实现上述两个 Multi-Tenancy 接口(interface)之一。

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

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 在单独的类中注入(inject)构造函数:

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 的实例类将被注入(inject) responseHandlers多变的。同样,当 MediatorPipeline类拦截了返回类型为 IQueryable<IMultitenantOptional> 的响应, MultiTenantOptionalHandler 的实例类将被注入(inject) responseHandlers变量。

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

这一切都在构建和工作。有点儿。这都是 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",

第一次执行在堆栈跟踪中更深:

"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, IEnumerable1 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.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(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
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)",

虽然第二次执行在同一个地方有这个:

"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, IEnumerable1 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 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(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
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)",

第三次和所有后续执行均顺利通过,返回预期的有效负载。

非常感谢 Autofac 专家的帮助。

编辑:

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

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

编辑 2:

这最终导致了对 Autofac 存储库的拉取请求,到目前为止,该存储库已合并到开发中,有可能被包含在下一个 Autofac 版本中。请在此处跟踪集成进度https://github.com/autofac/Autofac/pull/796 .

最佳答案

正如您在堆栈跟踪中看到的,异常是在 TryBindService 中引起的来自 OpenGenericServiceBinder 的方法类(class)。此方法尝试为给定的类型参数找到兼容的服务。它调用 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具有另一种类型约束的实现。

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.

我不知道你的方法是否是最佳实践,但我没有发现它有任何问题,我想如果 ParameterCompatibleWithTypeConstraint 应该可以正常工作方法将按预期工作。

关于c# - 解决实现相同接口(interface)的泛型类型时的 Autofac 问题,需要 Resolve() 两次或更多次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39291046/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com