Search code examples
c#.net-coredependency-injection

How To Register Interface<type, type>


I am attempting to setup dependency injection in my .Net 8 app and want it to scan and register based on naming convention. Where I am stuck is I cannot figure out how to register IFoo<in T1, out T2> which someone created implementations of to setup custom mapping of objects. The specific implementations inherit from interfaces that inherit from IFoo.

Here is my DI code so far:

public static void ConfigureServices(IServiceCollection services, Assembly assembly)
{
    var types = assembly.GetTypes()
        .Where(t => t.GetInterfaces().Any(i => i.IsGenericType)).ToList();

    foreach (var type in types)
    {
        var interfaces = type.GetInterfaces();

        foreach (var @interface in interfaces)
        {
            // Register the interface mapping to the type in DI container
            services.AddTransient(@interface, type);

            // Register a factory for the Funcs
            services.AddTransient(typeof(Func<>).MakeGenericType(@interface),
                provider => new Func<object>(() => provider.GetService(@interface)));
        }
    }
}

Here is an example of how the classes are setup:

 public interface ISpecificFoo : IFoo<Member, SpecificFoo.Output>
 {
 }

 public class SpecificFoo : ISpecificFoo 
 {
     public class Output
     {
     }
 }

The error I am getting so far is:

System.ArgumentException: 'Cannot instantiate implementation type 'ISpecificFoo' for service type 'IFoo`2


Solution

  • You're not filtering out interface types, so if you have any interfaces that extend an IFoo<in T1, out T2> (as ISpecificFoo does), then that will be incorrectly registered as an implementation type.

    You could do this:

    var types = assembly
        .GetTypes()
        .Where(t => !t.IsAbstract) // Don't consider interfaces (or abstract classes).
        .Where(t => t.GetInterfaces().Any(i => i.IsGenericType))
        .ToList();
    

    Now only instantiable types will be registered e.g. SpecificFoo.

    To restrict this even further, you could specifically target just IFoo<in T1, out T2> as the service type:

    var interfaces = type
        .GetInterfaces()
        .Where(i =>
            i.IsGenericType &&
            i.GetGenericTypeDefinition() == typeof(IFoo<,>));