Register multiple implementations for one open Generic with SimpleInjector
I am using SimpleInjector as an IoC container. I have developed several (excuse me, probably using the wrong term) partially closed implementations, for one common interface.
I would like to be able to query the generic interface and based on the types provided, SimpleInjector will return the correct class implementation. (I can figure out this might be no-no, since implementations can overlap if done wrong, etc., but I would still like to know if this can be done.)
Based on the code snippets below, how can I configure SimpleInjector to return an instance to me ITrim<Ford, Green>
?
Base class level:
public interface IColour { }
public interface IVehicle { }
public interface ITrim<TVehicle, TColour>
where TVehicle : IVehicle
where TColour : IColour
{
void Trim(TVehicle vehicle, TColour colour);
}
public abstract class TrimVehicle<TVehicle, TColour> : ITrim<TVehicle, TColour>
where TVehicle : IVehicle
where TColour : IColour
{
public virtual void Trim(TVehicle vehicle, TColour colour) { }
}
Middle tier providing a common code for the vehicle type:
public abstract class Green : IColour { }
public abstract class Blue : IColour { }
public abstract class Car : IVehicle { }
public abstract class Bike : IVehicle { }
public abstract class TrimCar<TCar, TColour> : TrimVehicle<TCar, TColour>
where TCar : Car
where TColour : IColour
{
public override void Trim(TVehicle vehicle, TColour colour)
{
base.Trim(vehicle, colour);
}
}
public abstract class TrimBike<TBike, TColour> : TrimVehicle<TBike, TColour>
where TBike : Bike
where TColour : IColour
{
public override void Trim(TVehicle vehicle, TColour colour)
{
base.Trim(vehicle, colour);
}
}
Final layer providing more specific implementations:
public class Ford : Car { }
public class TrimFord<TFord, TColour> : TrimCar<TFord, TColour>
where TFord : Ford
where TColour : IColour
{
public override void Trim(TVehicle vehicle, TColour colour)
{
base.Trim(vehicle, colour);
}
}
public class Yamaha : Bike { }
public class TrimYamaha<TYamaha, TColour> : TrimBike<TYamaha, TColour>
where TYamaha : Yamaha
where TColour : IColour
{
public override void Trim(TVehicle vehicle, TColour colour)
{
base.Trim(vehicle, colour);
}
}
source to share
A very difficult question with a very simple answer:
container.RegisterOpenGeneric(typeof(ITrim<,>), typeof(TrimCar<,>));
container.RegisterOpenGeneric(typeof(ITrim<,>), typeof(TrimFord<,>));
container.RegisterOpenGeneric(typeof(ITrim<,>), typeof(TrimYamaha<,>));
This works because Simple Injector respects any generic type constraint of a given type (or at least any of them handles any nasty bizarre type constraints I could think of). Therefore, as long as you make sure that the registered public generic types ( TrimCar
, TrimFord
and TrimYamaha
) do not overlap, it will work as expected.
If they overlap, the container will throw an exception, informing you that multiple event watchers ResolveUnregisteredType
have tried to register {of some type}.
While you should be careful that using these overlapping types does not complicate your application, in general I find that using type type constraints is very convenient and I use it all the time (especially when registering decorators).
UPDATE
If you have a set of custom decorators, the current implementation RegisterManyForOpenGeneric
cannot distinguish them from "normal" types and tries to register them. If you don't want that, you can register your types like this:
var types = OpenGenericBatchRegistrationExtensions.GetTypesToRegister(
typeof(ITrim<,>), typeof(ITrim<,>).Assembly)
.Where(type => !type.Name.EndsWith("Decorator");
container.RegisterManyForOpenGeneric(typeof(ITrim<,>), types);
In extension methods, RegisterManyForOpenGeneric
also used GetTypesToRegister
internally.
UPDATE 2
RegisterManyForOpenGeneric
Simple Injector 2 method now recognizes non-standard decorators, so as of v2 you can simply do this:
container.RegisterManyForOpenGeneric(
typeof(ITrim<,>),
typeof(ITrim<,>).Assembly);
Much easier, isn't it?
source to share