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);
    }
}

      

+3


source to share


1 answer


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?

+4


source







All Articles