Search code examples
c#dependency-injectionautofac

How to register a generic dependency in Autofac when I dont know the Types in advance?


In the below program, I am trying to figure out a way of I can register the instance and instance1 as singleton dependency in Autofac. In the main function, I only get the types as "ActualType" so I have to use MakeGeneric to create an instance from them.

Can someone please take a look at this?

void Main()
{
    string type1 = "System.String";
    string type2 = "System.String";

    Type columnFetcherType = typeof(Foo<,>).MakeGenericType(Type.GetType(type1), Type.GetType(type2));
    ConstructorInfo ctor = columnFetcherType.GetConstructors().FirstOrDefault();
    var instance1 = ctor.Invoke(new object[] { 1, 2 });

    // I want to register instance1 as singleton with Autofac, so that when I resolve Test, It will inject the Foo<string, string> instance which I registered.

    type1 = "System.Int32";
    type2 = "System.Int32";

    columnFetcherType = typeof(Foo<,>).MakeGenericType(Type.GetType(type1), Type.GetType(type2));
    ctor = columnFetcherType.GetConstructors().FirstOrDefault();
    var instance2 = ctor.Invoke(new object[] { 1, 2 });

    // I want to register instance2 as singleton with Autofac, so that when I resolve Test1, It will inject the Foo<int, int> instance which I registered.

}

// You can define other methods, fields, classes and namespaces here


public interface IFoo<Tkey,Tvalue> 
{
    void Print();
}

public class Foo<Tkey, Tvalue> : IFoo<Tkey, Tvalue>
{
    private int _a ;
    private int _b ;
    
    public Foo(int a, int b)
    {
        this._a = a;
        this._b = b;
    }
    
    public void Print()
    {
        Console.WriteLine(this.GetType());
    }
}

public interface ITest {}

public class Test : ITest
{
    IFoo<string, string> _param1;
    
    public Test(IFoo<string, string> param1)
    {
        this._param1 = param1;
    }
}

public class Test1 : ITest
{
    IFoo<int, int> _param1;

    public Test1(IFoo<int, int> param1)
    {
        this._param1 = param1;
    }
}

Thanks Bunch.


Solution

  • It isn't much different from registering with the generics, you just need to do more work with reflection and types. You almost had it.

    I'll show you how to do one; the others are the exact same pattern.

    // The trick here is that you need...
    // - the concrete object type
    // - the constructor (so you can create the instance)
    // - the instance
    // - the interface type (because the constructor parameter isn't a Foo<T,U>, it's IFoo<T,U>)
    // ...so:
    
    // Get the generic parameter.
    var stringType = Type.GetType("System.String");
    
    // Get the concrete type.
    var closedGeneric = typeof(Foo<,>).MakeGenericType(stringType, stringType);
    
    // Get the constructor.
    var ctor = closedGeneric.GetConstructors()[0];
    
    // Create your instance.
    var instance = ctor.Invoke(new object[] { 1, 2 });
    
    // Get the interface that the constructor wants.
    var interfaceType = typeof(IFoo<,>).MakeGenericType(stringType, stringType);
    
    // Now...
    var builder = new ContainerBuilder();
    
    // Register the thing that consumes the instance.
    builder.RegisterType<Test>();
    
    // Register your instance, but add the .As() so you can resolve as the interface.
    builder.RegisterInstance(instance).As(interfaceType);
    
    // Build and resolve. Done!
    var container = builder.Build();
    container.Resolve<Test>();