Allow resolving variant implementations as well

Nov 10, 2014 at 2:17 PM
In one of my old articles I describe the reason why Simple Injector does not resolve variant implementations out of the box. Basically, the reasoning is explained in the following paragraph:
For instance, what should the container do when an unregistered (variant) service type is requested? It should probably go look for a registration that is assignable from that requested type. But what if there are multiple compatible registrations? Should the container throw an exception? Should the container use the registration that is closest in the inheritance hierarchy to the requested type? And what to do when there are multiple types that are just as close? Or should the container return a list of all compatible registrations (possibly wrapped in a composite). And should this behavior hold for all types in the container, should it just be enabled for a set or types, or should the behavior differ per registration?
This reasoning is correct and IMO still holds, but the arguments against support are mainly based on resolving a single instance.

However, I think there is no good reason for not resolving all assignable implementations when requesting a collection. I question whether there is ever a reason not to resolve all assignable implementations in case the used defined his abstraction with the variant keywords 'in' or 'out'. In other words, I think it is safe to assume that IF the user defined the abstraction variant, he wants to resolve all assignable implementations, and if he didn't define the abstraction variant, he only wants to returns the exact matches.

Can you think of a reason to not wanting to do this, or would it be good to add this feature to Simple Injector; that is: let GetAllInstances automatically resolve all assignable instances that are registered using RegisterAll; not only the direct mappings.
Nov 10, 2014 at 3:54 PM
I had assumed that GetAllInstances would return all assignable instances but as the concept of variance/in/out is something I don't fully understand it's a naïve assumption!

Can you show an example of what I may be wrongly assuming would already be resolved and actually wouldn't be resolved but would be resolved with the change?
Nov 10, 2014 at 5:34 PM
Given the following interfaces and classes:
// Generic handler interface
public interface IEventHandler<in TEvent> { }

// Events
public class CustomerMovedEvent { }
public class CustomerMovedAbroadEvent : CustomerMovedEvent { }
public class SpecialCustomerMovedEvent : CustomerMovedEvent { }

// Handler implementations
public class CustomerMovedEventHandler : IEventHandler<CustomerMovedEvent> { }
public class NotifyStaffWhenCustomerMovedEventHandler : IEventHandler<CustomerMovedEvent> { }
public class CustomerMovedAbroadEventHandler : IEventHandler<CustomerMovedAbroadEvent> { } 
And the following registration:
var container = new Container();

container.RegisterAll(typeof(IEventHandler<>), new[]
When calling GetAllInstances<IEventHandler<CustomerMovedEvent>>(), the v2.6.1 of Simple Injector will return CustomerMovedEventHandler and NotifyStaffWhenCustomerMovedEventHandler.

However, when allowing covariant and contravariant types to be resolved, that call will return besides CustomerMovedEventHandler and NotifyStaffWhenCustomerMovedEventHandler, CustomerMovedAbroadEventHandler as well.
Nov 10, 2014 at 7:26 PM
As you have already stated, this is something we can control
ever a reason not to resolve all assignable implementations in case the used defined his abstraction with the variant keywords 'in' or 'out'
so the only reason not to is because it is a breaking change.
Nov 10, 2014 at 8:20 PM
I agree, this is a breaking change. Developers might be depending on Simple Injector to not resolve varient implementations.

Chances are slim however, that this feature will break anyone's code, because:
  1. I only want to enable this in case the registration is done using RegisterAll.
  2. RegisterAll only allows open generic registrations since 2.6 which has been released a month ago.
  3. It will only affect developers that marked their interface with in and out keywords.
Another reason to add this feature, is because filtering out the variant implementations out of the collection if you don't want them, is muxh easier than adding the variant implementations if Simple Injector doesn't add them automatically.
Nov 10, 2014 at 8:53 PM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Nov 10, 2014 at 10:06 PM
That was an incredibly easy fix. It boiled down to a simple change in the ExtensionHelpers.GetClosedGenericImplementationsFor() method.
Jan 15, 2015 at 10:21 AM
A new version of Simple Injector (v2.7) has been released. This version contains the discussed feature.
Marked as answer by dot_NET_Junkie on 2/2/2015 at 12:15 PM