Dependency Injection of open generics with arguments

Viewed 1101

This article talks about how to register Generic interfaces in .Net Core. However I have a generic interface which has multiple arguments and am having trouble figuring out registeration and constructor injection.

My Interface has 4 arguments

public class TestImplementation 
{
    // Try to inject IRepository here ??????
    public TestImplementation(.......)
    {
        ...
    }
}

public class Repository : IRepository<Test1, Test2, Test3, Test4> 
{
    ...
}

public interface IRepository<T, U, V, W> where T : ITemplate1 where U : ITemplate2,...  
{
    ...
}

If I try to inject the Interface to any class, it gives me error as the interface doesnt get resolved even using below code in other part of the code

services.GetService(typeof(IRepository<,,,>))

I tried using constructor injection but it makes compiler unhappy (Unable to resolve service for type '....Interface....' while attempting to activate xxxx) as I want to keep the Interface open. I however resolve the interface in the code

2 Answers

The proper way is to not inject the Repository Service. It should just be used as a template with functionality. You then create an additional class that inherits from the Repository class and an interface that inherits from IRepository. That way you can assign the values of the generic values and then inject it in a neat and controlled way.

At first this pattern might seem like a bit of extra work, but it allows for custom functionality for each table repository, keeps it clear which table you are working with and allows for easy replacement for different databases. See example below:

So as you had it, create your Repository and IRepository interface:

public abstract class Repository<T, U, V, W> : IRepository<T, U, V, W> 
{
    ...
}

public interface IRepository<T, U, V, W> where T : ITemplate1 where U : ITemplate2,...  
{
    ...
}

Now create an interface for your specific table. First the interface:

public interface ISomeTableRepository : IRepository<input_1, input_2, ...> {}

Now create the class repository:

public class SomeTableRepository : Repository<input_1, input_2,...> , ISomeTableRepository {}

Now you register these the new repository that doesn't have inputs in your Startup.cs file.

services.AddScoped<ISomeTableRepository, SomeTableRepository> ();

Now you can easily inject it without the need to add parameters:

public class TestImplementation 
{
    readonly ISomeTableRepository _someTable;

    
    public TestImplementation(ISomeTableRepository someTable)
    {
        _someTable = someTable;
    }
}

Whichever members you need to have in the unbound generic type you put into a base type which the derived type implements.

You can use either object or dynamic for the values of an unknown type.

See this example:

public interface IRepository
{
    object TV { get; set; }
    object UV { get; set; }
    object VV { get; set; }
    object WV { get; set; }
}

public interface IRepository<T, U, V, W>: IRepository
{
    object IRepository.TV { get => TV; set => TV = (T)value; }
    object IRepository.UV { get => UV; set => UV = (U)value; }
    object IRepository.VV { get => VV; set => VV = (V)value; }
    object IRepository.WV { get => WV; set => WV = (W)value; }
    new T TV { get; set; }
    new U UV { get; set; }
    new V VV { get; set; }
    new W WV { get; set; }
}

Then, you can do:

services.GetService(typeof(IRepository))

without any issue.

https://dotnetfiddle.net/kwoOOe

Related