Search code examples
c#dependency-injectioninversion-of-controlioc-containersimple-injector

Simple Injector: Registering open generic type with constructor parameter


I have created a generic type

public interface IContext<T> {}

And, I have a type implemented that (with a constructor with parameters)

public class Context<T> : IContext<T> { public Context(string url, string key) { } ... }

I want to register with simple injector. With the below code, I dont know how to pass the values for the constructor

container.Register(typeof(IContext<>), typeof(Context<>))

This one shows a way if I have a type passed in the constructor parameter. However, for me its primitive types only. Looks like by overriding construction resolution behaviour I may achieve this. But, dont really get the idea of how shall I leverage it. Can someone guide me to find an appropriate way to register this?


Solution

  • When dealing with primitive dependencies into an open-generic registration, the typical solution is to extract the set of configuration value(s) into a DTO and inject that DTO into the type's constructor; this allows you to register that new configuration object as singleton into the container:

    I have created a generic type

    public interface IContext {}

    And, I have a type implemented that (with a constructor with parameters)

    public class ContextConfiguration
    {
        public readonly string Url;
        public readonly string Key;
        public ContextConfiguration(string url, string key) { ... }
    }
    
    public class Context<T> : IContext<T>
    {
        public Context(ContextConfiguration config)
        {
        }
        ...
    }
    
    // Configuration
    container.RegisterSingleton(new ContextConfiguration(...));
    container.Register(typeof(IContext<>), typeof(Context<>));
    

    In case you can't change the constructor of that type, you create a sub class of that type that you place inside the Composition Root. This sub type again uses this configuration DTO:

    // Part of the Composition Root
    private class ContextConfiguration
    {
        public readonly string Url;
        public readonly string Key;
        public ContextConfiguration(string url, string key) { ... }
    }
    
    private class CompositionRootContext<T> : Context<T>
    {
        public Context(ContextConfiguration config) : base(config.Url, config.Key)
        {
        }
        ...
    }
    
    // Configuration
    container.RegisterSingleton(new ContextConfiguration(...));
    container.Register(typeof(IContext<>), typeof(CompositionRootContext<>));
    

    If that Context<T> is sealed, you could override the parameter injection behavior, but in general, in that case you are dealing with a type that is defined by an external library. For external types it is generally better to hide them behind an application-tailored abstraction (according to the DIP). Instead of letting application code depend on IContext<T>, you let the application depend on an interface that is defined by the application. As part of your Composition Root, you would implement an Adapter that adapts the application-specific interface to Context<T>. The constructor of that adapter would -again- be able to use this configuration DTO.