When a class implements same generic interface multiple times with different arguments then Autofac not always resolve all implemented interfaces. It is sensitive to the order of interface declaration.
Changing order of interface declaration could break interface resolution
Below are tests with 2 dummy classes FirstInterfaceTypeParameterIsNotGeneric and FirstInterfaceTypeParameterIsGeneric.
The difference between them is in order of interface declaration.
Both classes implement same interface multiple times with different generic arguments.
There are 2 green tests that could resolve both interfaces for class FirstInterfaceTypeParameterIsNotGeneric.
There is 1 red test - GivenFirstInterfaceTypeParameterIsGeneric_WhenResolveNotGeneric_ThenResolved for class FirstInterfaceTypeParameterIsGeneric.
```c#
public class SameGenericInterfaceWithDifferenceParametersTests
{
private interface IHandler
{
void Handle(T input);
}
private class FirstInterfaceTypeParameterIsNotGeneric<TDocument> :
IHandler<TDocument>,
IHandler<List<TDocument>>
{
public void Handle(TDocument input) => throw new NotImplementedException();
public void Handle(List<TDocument> input) => throw new NotImplementedException();
}
private class FirstInterfaceTypeParameterIsGeneric<TDocument> :
IHandler<List<TDocument>>,
IHandler<TDocument>
{
public void Handle(TDocument input) => throw new NotImplementedException();
public void Handle(List<TDocument> input) => throw new NotImplementedException();
}
[Fact]
public void GivenFirstInterfaceTypeParameterIsNotGeneric_WhenResolveList_ThenResolved()
{
IContainer context = CreateContext(typeof(FirstInterfaceTypeParameterIsNotGeneric<>));
context.Resolve<IHandler<List<object>>>();
}
[Fact]
public void GivenFirstInterfaceTypeParameterIsNotGeneric_WhenResolveNotGeneric_ThenResolved()
{
IContainer context = CreateContext(typeof(FirstInterfaceTypeParameterIsNotGeneric<>));
context.Resolve<IHandler<object>>();
}
[Fact]
public void GivenFirstInterfaceTypeParameterIsGeneric_WhenResolveGeneric_ThenResolved()
{
IContainer context = CreateContext(typeof(FirstInterfaceTypeParameterIsGeneric<>));
context.Resolve<IHandler<List<object>>>();
}
[Fact]
public void GivenFirstInterfaceTypeParameterIsGeneric_WhenResolveNotGeneric_ThenResolved()
{
IContainer context = CreateContext(typeof(FirstInterfaceTypeParameterIsGeneric<>));
context.Resolve<IHandler<object>>();
}
private static IContainer CreateContext(Type type)
{
var builder = new ContainerBuilder();
builder.RegisterGeneric(type)
.AsImplementedInterfaces();
var context = builder.Build();
return context;
}
}
## Expected Behavior
Order of interface declaration should be irrelevant
<!-- Describe what you expected to happen. -->
## Exception with Stack Trace
```text
Autofac.Core.Registration.ComponentNotRegisteredException
The requested service 'Autofac.Specification.Test.ImplementSameInterfaceTests+IHandler`1[[System.Object, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]' has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.
at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters) in D:\sources\autofac\Autofac\src\Autofac\ResolutionExtensions.cs:line 881
at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType, IEnumerable`1 parameters) in D:\sources\autofac\Autofac\src\Autofac\ResolutionExtensions.cs:line 342
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context, IEnumerable`1 parameters) in D:\sources\autofac\Autofac\src\Autofac\ResolutionExtensions.cs:line 294
at Autofac.ResolutionExtensions.Resolve[TService](IComponentContext context) in D:\sources\autofac\Autofac\src\Autofac\ResolutionExtensions.cs:line 277
at Autofac.Specification.Test.ImplementSameInterfaceTests.GivenFirstInterfaceIsGeneric_WhenResolveNotGeneric_ThenResolved() in D:\sources\autofac\Autofac\test\Autofac.Specification.Test\GenericParametersTests.cs:line 61<!-- If you see an exception, put the WHOLE THING here. -->
Autofac: 5.0.0
@KirillSelyak thanks for opening that issue, good catch.
@tillig @alexmg @alistairjevans I've just verified it that's still a problem with v6, which is the case. From my understanding that should work?!
I checked it with this commit. Will look into a bit.
I updated the original issue description with some formatting for readability. Hopefully I didn't mess it up. I admit it's sort of confusing because _all_ the interfaces shown are generics, it's just that the _type parameter_ on one of the generics is _also_ generic.
Odd that it isn't working, it should.
Odd that it isn't working, it should.
Just did a bit of debugging. It looks like that the HashSet<Service> in RegistrationData considers the type to be equal and is therefore not adding it as a registration.

In fact, if you do a Distinct() on the IEnumerable<Service> passed to RegistrationData.AddService, you will get only a single type instead of two, which are passed to the function.

Which makes sense because RegistrationBuilder{TLimit,TActivatorData,TRegistrationStyle}.As(params Type[] services) get the generic type definition, which is the same for both services. That's why the order is important. What ever is added to the hashset first, can be resolved. Which implicitly relies on the implementing interface order.
@tillig , correct. You made bug description better. Generic type parameters are key to the bug.
I have updated tests with better naming as well
This appears to be caused by the OpenGenericServiceBinder not taking all interfaces on the implementation type into account during the binding process. I haven't had time to dive deep on this but given the nature of that code it could take some digging to get to the bottom of the problem.
@alsami I had a bit of time to look at this tonight and while debugging the issue managed to produce a fix. The issue was the OpenGenericServiceBinder not taking all interfaces on the implementation type into account. I extended the unit tests to assert the resolved closed generic types are correct and included variations for AsImplementedInterfaces and explicit service type (typeof(IHandler<>)). I also discovered a code path and error message that was never being hit so removed the associated code and error message resource.
@alsami I had a bit of time to look at this tonight and while debugging the issue managed to produce a fix. The issue was the
OpenGenericServiceBindernot taking all interfaces on the implementation type into account. I extended the unit tests to assert the resolved closed generic types are correct and included variations forAsImplementedInterfacesand explicit service type (typeof(IHandler<>)). I also discovered a code path and error message that was never being hit so removed the associated code and error message resource.
Thanks for fixing it, I wish I would have been faster, wanted to look more into it this weekend. Did you push the changes already?
Sorry @alsami, there was no rush. I just happened to have a moment free and went in to do some deeper analysis for you expecting the worst based on past experiences. By some miracle a few tweaks later and the issue was fixed. I think that must be a first with the OpenGenericServiceBinder. The changes are in the develop branch if you want to check them out.
@KirillSelyak You can test this change out using the package from the MyGet feed if you want to validate it resolves your issue.
Install-Package Autofac -Version 6.0.0-develop-01147 -Source https://www.myget.org/F/autofac/api/v3/index.json