Search code examples
c#dependency-injectioninversion-of-controlsimple-injector

SimpleInjector - how to register many implementations of a generic type with LifetimeScopeLifestyle?


While answering this question on SO I couldn't figure out the best technique for registering many implementations of a generic type with an instance of LifetimeScopeLifestyle within SimpleInjector.

The recommended method for this form of registration is something like this:

container.RegisterManyForOpenGeneric(typeof(IRepository<>), 
    typeof(IRepository<>).Assembly);

But this does not allow an instance of LifetimeScopeLifestyle to be passed in.

Below is what I came up with but I know it's not resilient enough as it is checking for any generic interface, not specifically IRepository<>. Can anyone tell me how to do this?

public static void Configure(Container container)
{
    var lifetimeScope = new LifetimeScopeLifestyle();

    container.Register<IUnitOfWork, UnitOfWork>(lifetimeScope);

    //this query needs improvement
    var registrations =
        from type in typeof(IRepository<>).Assembly.GetExportedTypes()
        where typeof(IRepository).IsAssignableFrom(type)
            && type.IsClass
            && !type.IsAbstract
        from service in type.GetInterfaces()
        where service.IsGenericType
        select new { Service = service, Implementation = type };

    foreach (var registration in registrations)
    {
        container.Register(registration.Service, 
            registration.Implementation, lifetimeScope);
    }
}

Solution

  • TLDR:

    container.RegisterManyForOpenGeneric(
        typeof(IRepository<>),
        lifetimeScope, 
        typeof(IRepository<>).Assembly);
    

    First of all, your query is wrong. It should have been:

    var registrations =
        from type in
            typeof(IRepository<>).Assembly.GetExportedTypes()
        where !service.IsAbstract
        where !service.IsGenericTypeDefinition
        from @interface in type.GetInterfaces()
        where @interface.IsGenericType
        where @interface.GetGenericTypeDefinition() ==
            typeof(IRepository<>)
        select new { Service = @interface, Impl = type };
    

    Second, the framework contains a GetTypesToRegister method to fetch these types for you, which excludes decorator types:

    var repositoryTypes =
        OpenGenericBatchRegistrationExtensions.GetTypesToRegister(
            container, typeof(IRepository<>), 
            typeof(IRepository<>).Assembly);
    
    var registrations =
        from type in repositoryTypes
        from @interface in type.GetInterfaces()
        where @interface.IsGenericType
        where @interface.GetGenericTypeDefinition() ==
            typeof(IRepository<>)
        select new { Service = @interface, Impl = type };
    

    But it gets better, the container contains an overload of the RegisterManyForOpenGeneric method that takes a callback delegate that allows you to do the registration as follows:

    container.RegisterManyForOpenGeneric(
        typeof(IRepository<>),
        (service, impls) =>
        {
            container.Register(service, impls.Single(),
                lifetimeScope);
        }, 
        typeof(IRepository<>).Assembly);
    

    But most importantly, the framework contains RegisterManyForOpenGeneric overloads that takes in an Lifetime. So you are able to simplify your registration to the following:

    container.RegisterManyForOpenGeneric(
        typeof(IRepository<>),
        lifetimeScope, 
        typeof(IRepository<>).Assembly);