Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic parameters constrained as generic interfaces fail to resolve. #330

Closed
alexmg opened this issue Jan 22, 2014 · 4 comments
Closed
Labels

Comments

@alexmg
Copy link
Member

alexmg commented Jan 22, 2014

From [email protected] on June 15, 2011 12:01:15

What steps will reproduce the problem? Here is the situation with a failing test.

    public interface IConstraint<T> { }
    public class Constraint<T> : IConstraint<T> { }

    public interface IGenericConstraint<T1, T2> 
        where T2 : IConstraint<T1> 
    {
    }
    public class GenericConstraint<T1, T2> : IGenericConstraint<T1, T2> 
        where T2 : IConstraint<T1> 
    {
    }

    [Test]
    public void GenericConstraints()
    {
        var builder = new ContainerBuilder();

        builder.RegisterGeneric(typeof(Constraint<>))
            .AsImplementedInterfaces();

        builder.RegisterGeneric(typeof(GenericConstraint<,>))
            .AsImplementedInterfaces();

        var container = builder.Build();

        var target = container.Resolve<IGenericConstraint<int, IConstraint<int>>>();
    } What is the expected output? What do you see instead? This above should resolve just fine.  The problem is in ParameterCompatibleWithTypeConstraint in TypeExtensions.cs

    static bool ParameterCompatibleWithTypeConstraint(Type parameter, Type constraint)
    {
        return 
            constraint.IsAssignableFrom(parameter) ||
            (parameter.IsClass &&
               Traverse.Across(parameter, p => p.BaseType)
                   .Concat(parameter.GetInterfaces())
                   .Any(p => p.GUID == constraint.GUID));
    }

If the "parameter.IsClass" condition is removed and Traverse is allowed to execute then all works correctly.

Original issue: http://code.google.com/p/autofac/issues/detail?id=330

@alexmg
Copy link
Member Author

alexmg commented Jan 22, 2014

From [email protected] on June 15, 2011 03:04:48

Interesting - looks on the surface that this is a good change. I'm not 100% sure of the original reason for the .IsClass, but the generic constraint reflection APIs are quirky.

If all the tests pass with the check removed I guess by definition it is safe :) .. If anything tricky shows up we might be able to use (parameter.IsClass || parameter.IsInterface) as an alternative.

Cheers,
Nick

@alexmg
Copy link
Member Author

alexmg commented Jan 22, 2014

From [email protected] on June 19, 2011 10:15:33

Thanks for the response Nick. The unit tests passed when I ran it.

After looking at the code... I think that though this fix works, the constraint compatibility checks are still not expansive enough to truly choose the correct registration. Taking the example above, the constraint checking code doesn't recurse into the generic parameter of the interface:

where T2 : IConstraint

In other words, I could have had IConstraint there, and I believe the code would report that the constraint passes, even though I truly only want to allow IConstraint. (I haven't written a test for this yet as I am not in front of the code at the moment, but I wanted to point out the possibility before you make any decisions.)

@alexmg
Copy link
Member Author

alexmg commented Jan 22, 2014

From [email protected] on June 24, 2011 09:15:04

Another note... Even though I pointed out that the constraint matching code in general should be more robust in its search, getting this surgical fix into the build would still help a great deal, even without adding the more complex logic.

@alexmg
Copy link
Member Author

alexmg commented Jan 22, 2014

From [email protected] on July 02, 2011 17:25:17

I've made the suggested fix - the test came down to this:

var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof (Constrained<,>));
var container = builder.Build();
Assert.That(container.IsRegistered<Constrained<int, IConstraint>>());

We'll need to look into this further in the future, but I'm happy to close this now and deal with unsupported cases as they arrive.

Thanks again! Nick

Status: Fixed

@alexmg alexmg closed this as completed Jan 22, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant