Search code examples
c#.netdependency-injection

.Net DI register generic and more specific generic


I have an interface IFlattener<T> and I want to create a class ListFlattener<T> : IFlattener<IList<T>> to iterate over obejcts and call their flattener on them.

I have a number of flatteners ZoneFlattener : IFlattener<Zone> ZoneGroupFlattener : IFlattener<ZoneGroup> OperationFlattener : IFlattener<Operation>

All of which are registered

services.AddScoped<IFlattener<Zone>, ZoneFlattener>()
services.AddScoped<IFlattener<ZoneGroup>, ZoneGroupFlattener>()
services.AddScoped<IFlattener<Operation>, OperationFlattener>()

In a number of places I have a List<Zone> or List<ZoneGroup> and instead of doing the same loop over them all every time I want to have IFlattener<IList<T>> to do it for me.

public class ListFlattener<T>(IFlattener<T> flattener) : IFlattener<IList<T>> 
{
} 

How can I register this so constructors can take an IFlattener<IList<T>> and get a ListFlattener<T> that would in turn take an IFlattener

I've tried to register with the generics services.AddScoped(typeof(IFlattener<>), typeof(ListFlattener<>)) This does not work, it cannot resolve the dependency at runtime.

I know the alternative is to use an IListFlattener<T> and register that instead or perhaps use a different DI container which I would want to avoid.


Solution

  • I've tried to register with the generics services.AddScoped(typeof(IFlattener<>), typeof(ListFlattener<>)) This does not work

    That is correct. MS.DI can't handle this more-advanced type of mapping. The generic type arguments of the abstraction must line up exactly with that of the implementation.

    Instead, the only way to solve this is by making each closed-generic registration explicitly:

    services.AddScoped<IFlattener<IList<Zone>>, ListFlattener<Zone>>();
    services.AddScoped<IFlattener<IList<ZoneGroup>>, ListFlattener<ZoneGroup>>();
    services.AddScoped<IFlattener<IList<Operation>>, ListFlattener<Operation>>();