I have an Autofac related issue with resolving generic types which implement the same interface, but have different type constraints. 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.
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. So I could have requests which have response types of e.g. IQueryable<Game> or IQueryable<Trophy>, where both Game and Trophy implement one of two above mentioned multitenancy interfaces.
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:
public class MediatorPipeline<TRequest, TResponse> : RequestHandler<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
public MediatorPipeline(
IResponseHandler<TRequest, TResponse>[] responseHandlers
)
}
Autofac configuration goes like this:
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. 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.
These injected classes post-process my responses if they are of these two specific IQueryable sub-types.
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:
"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",
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, IEnumerable1& 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, IEnumerable1 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, IEnumerable1 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(IEnumerable1 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, IEnumerable1 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, IEnumerable1 parameters)\r\n at 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, IEnumerable1& 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.WhereSelectEnumerableIterator2.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, IEnumerable1 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, IEnumerable1 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(IEnumerable1 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, IEnumerable1 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, IEnumerable1 parameters)\r\n at 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.
EDIT:
I created a console program which illustrates the issue:
https://gist.github.com/sebekz/4c658a5c7551ba5a2b3fd81488ea3ee7
EDIT 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. Please track the integration progress here https://github.com/autofac/Autofac/pull/796.
.AsImplementedInterfaces()is auto-discovering more than you expected, and finding a genericIRequest<TResponse>without constraints which gums up the entire container - Eris.AsImplementedInterfaces()at all, I was using a more specific.As(typeof(IResponseHandler<,>)), but the result is the same. First call fails, second call succeeds. If it failed all along, I would get it - ok, wrong config, but it IS working after the first exception with no issues, resolving as the correct class instance. I just don't get it. - Sebastian Zaklada