zcz527 / autofac

Automatically exported from code.google.com/p/autofac
Other
0 stars 0 forks source link

Open generic type registered as an implementor of contravariant interface #412

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
What steps will reproduce the problem?
1. Define a contravariant interface (eg. IValidator<out T>)
2. Define a generic implementation of that interface (eg. 
DataAnnotationValidator<T>)
3. Register DataAnnotationValidator as generic providing service IValidator<> 
4. Register ContravariantRegistrationSource in the container
5. Try to resolve IEnumerable<IValidator<string>> from the container

Expected result:
 - An enumerable containing a single element - DataAnnotationValidator<string>

Actual result:
 - An enumerable containing DataAnnotationValidators for every type in the hierarchy of the type supplied as generic parameter to IValidator<out T> interface. Eg. DataAnnotationValidator<string>, DataAnnotationValidator<object>

What version of Autofac are you using? 
 - Autofac.2.6.3.862

On what version of .NET/Silverlight?
 - .NET 4.0

Original issue reported on code.google.com by Eugene.S...@gmail.com on 5 Mar 2013 at 10:39

GoogleCodeExporter commented 8 years ago
I think this is functioning as designed.

Here's a more concrete test that I ran:

void Main()
{
  var builder = new ContainerBuilder();
  builder.RegisterSource(new ContravariantRegistrationSource());
  builder.RegisterGeneric(typeof(Concrete<>)).As(typeof(IContravariant<>));
  var container = builder.Build();
  var list = container.Resolve<IEnumerable<IContravariant<string>>>();
  foreach(var item in list)
  {
    Console.WriteLine(item.GetType());
  }
}

public interface IContravariant<in T> {}
public class Concrete<T> : IContravariant<T>{}

The output of that was:
typeof (Concrete<String>)
typeof (Concrete<Object>)
typeof (Concrete<IComparable>)
typeof (Concrete<ICloneable>)
typeof (Concrete<IConvertible>)
typeof (Concrete<IComparable<String>>)
typeof (Concrete<IEnumerable<Char>>)
typeof (Concrete<IEnumerable>)
typeof (Concrete<IEquatable<String>>)

Given the definition of "contravariant" 
(http://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science) 
and http://msdn.microsoft.com/en-us/library/dd799517.aspx) - converting from a 
more general type to a specific type, it makes sense that the contravariant 
source would provide a generic that has the specific concrete type AND the 
generics for all of the interfaces and base types the concrete type implements.

It also appears that's the intent of the source as written 
(https://code.google.com/p/autofac/source/browse/Core/Source/Autofac/Features/Va
riance/ContravariantRegistrationSource.cs).

I'm not entirely sure it was intended to resolve an IEnumerable of all of the 
contravariant types, but when doing it... it does appear to be functioning as 
designed.

I noticed in the issue report that the interface defined in step 1 of the repro 
was marked with an "out" generic type - that's covariant, not contravariant, 
but I'm not sure if that was just a typo.

Original comment by travis.illig on 6 Mar 2013 at 4:59

GoogleCodeExporter commented 8 years ago
Well, if you want to know why I am resolving IEnumerable<IValidator<T>>, I can 
try to explain.
I am trying to resolve and run all validators registered for a given type. One 
of them (and the one which is always there) is DataAnnotationValidator<T>. The 
described behavior results in that Autofac will resolve N validators 
constructed from DataAnnotationValidator<> that under the hood run the same 
code, thus duplicating validation errors.

I think that in the given circumstances Autofac should only return an 
implementation constructed for most specific type. Could you think of any case 
when you would want to resolve implementations for ALL types in hierarchy?

Original comment by Eugene.S...@gmail.com on 7 Mar 2013 at 8:09

GoogleCodeExporter commented 8 years ago
Yep, the interface being covariant was a typo. Sorry.

Original comment by Eugene.S...@gmail.com on 7 Mar 2013 at 8:11

GoogleCodeExporter commented 8 years ago
I can't think of a time where I've ever needed the 
ContravariantRegistrationSource, so I've never run into the trouble you're 
finding. I actually have applications that perform exactly the function you're 
mentioning and have no issues... but without that source in the pipeline.

The source itself is behaving as it should, and the collections support is also 
behaving; it's a weird conjunction between the two causing problems and I'm not 
sure it's something we could (or should) fix.

I might recommend taking the code from ContravariantRegistrationSource and 
modifying it to behave to your liking. Maybe it needs to be a 
CollectionAwareContravariantRegistrationSource or something like that.

Original comment by travis.illig on 7 Mar 2013 at 8:15