Search code examples
genericsdependency-injectionregistrationsimple-injector

How to register a TImpl that has one moregeneric type parameter than TService in SimpleInjector?


I am doing the following right now

container.Register<IDatabaseMapper<User>, DatabaseMapper<User, OracleException>>();
container.Register<IDatabaseMapper<Desk>, DatabaseMapper<Desk, OracleException>>();
container.Register<IDatabaseMapper<Commodity>, DatabaseMapper<Commodity, OracleException>>();

But i would like to do something like this

container.RegisterOpenGeneric(typeof(IDatabaseMapper<>), typeof(DatabaseMapper<,OracleException>));

Is this somehow possible?


Solution

  • Is this possible? Yes and no :-)

    typeof(DatabaseMapper<,OracleException>) is not valid C# code. You either have to supply all generic type arguments or none at all. So there is no way to inform the Container that it should fill in the missing TException type argument with a OracleException. So no, you can't do this.

    But yes, of course you can do this :-). Simply create a OracleExceptionDatabaseMapper<T> class that inherits from DatabaseMapper<T, OracleException> and use that type in the registration:

    // Helper class
    public class OracleExceptionDatabaseMapper<T>
        : DatabaseMapper<T, OracleException>
    {
    }
    
    // Registration
    container.RegisterOpenGeneric(typeof(IDatabaseMapper<>),
        typeof(OracleExceptionDatabaseMapper<>));
    

    This way the given implementation has only 1 generic type and that can be mapped to the single generic type argument of the given service interface.

    UPDATE

    Since Simple Injector 2.4 it is possible to register parial open generic types, but since this is still not supported by C# you will have to create the partial open generic type by hand as follows:

    Type databaseMapperType = typeof(DatabaseMapper<,>).MakeGenericType(
        typeof(DatabaseMapper<,>).GetGenericArguments().First(),
        typeof(OracleException));
    
    container.RegisterOpenGeneric(typeof(IDatabaseMapper<>), databaseMapperType);