Search code examples
c#asp.net-coredependency-injection.net-5open-generics

Injecting primitive type in constructor of generic type using Microsoft DI


I'm trying to use dependency injection to add a generic service that has constructor parameters. I Need to implement this, generically:

host.Services.AddSingleton<IService>(x => 
    new Service(x.GetRequiredService<IOtherService>(),
                x.GetRequiredService<IAnotherOne>(), 
                ""));

This is what I have working with open generics:

host.Services.AddSingleton(typeof(IGenericClass<>), typeof(GenericClass<>));

I have not been able to add constructor params with opengenerics. This is the class I want to add DI:

public class GenericClass<T> : IGenericClass<T> where T : class, new()
{
    private readonly string _connectionString;
    private readonly IGenericClass2<T> _anotherGenericInterface;
    private readonly IInterface _anotherInterface;
    public GenericClass(
        string connectionString,
        IGenericClass2<T> anotherGenericInterface,
        IInterface anotherInterface)
    {
        _connectionString = connectionString ??
            throw new ArgumentNullException(nameof(connectionString));
        _executer = anotherGenericInterface;
        _sproc = anotherInterface;
    }
}

Solution

  • With MS.DI, it's impossible to construct an open-generic registration using a factory method, just as you did with the IService registration.

    The solution here is to wrap all primitive constructor values into a Parameter Object, so the DI Container can resolve it as well. For instance:

    // Parameter Object that wraps the primitive constructor arguments
    public class GenericClassSettings
    {
        public readonly string ConnectionString;
        
        public GenericClassSettings(string connectionString)
        {
            this.ConnectionString =
                connectionString ?? throw new ArgumentNullExcpetion();
        }
    }
    

    The GenericClass<T>'s constructor can now depend on the new Parameter Object:

    public GenericClass(
        GenericClassSettings settings,
        IGenericClass2<T> anotherGenericInterface,
        IInterface anotherInterface)
    {
        _connectionString = settings.ConnectionString;
        _executer = anotherGenericInterface;
        _sproc = anotherInterface;
    }
    

    This allows you to register both the new parameter object and the open-generic class:

    host.Services.AddSingleton(new GenericClassSettings("my connection string"));
    
    host.Services.AddSingleton(typeof(IGenericClass<>), typeof(GenericClass<>));