Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency injection for generic class

I have a generic class and a generic interface like this:

public interface IDataService<T> where T: class
{
    IEnumerable<T> GetAll();
}

public class DataService<T> : IDataService<T> where T : class
{
    public IEnumerable<T> GetAll()
    {
        return Seed<T>.Initialize();
    }
}

public static IEnumerable<T> Initialize()
{
    List<T> allCalls = new List<T>();
    ....
    return allCalls;
}

Now in my StartUp.cs I'm hooking up the class and interface

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient(typeof(IDataService<>), typeof(DataService<>));
    ...
}

When I try to use it in my e.g. Repository.cs its always null.

public class Repository<T> : IRepository<T> where T : class
{
    private readonly IDataService<T> _dataService;

    public Repository(IDataService<T> dataService)
    {
        _dataService = dataService;
    ...
    }
    ...
}

EDIT Here is the requested Repository Interface and class

public interface IRepository<T> where T : class
{
    double GetCallPrice(T callEntity, Enum billingType);
    double GetCallPriceFromIdAndBillingType(int id, Enum billingType);
}

And the Repository.cs class

public class Repository<T> : IRepository<T> where T : class
{
    private readonly IDataService<T> _dataService;
    private IEnumerable<T> _allCalls;

    public Repository(IDataService<T> dataService)
    {
        _dataService = dataService;
    }

    public double GetCallPrice(int id)
    {
        _allCalls = _dataService.GetAllCalls();
        ...
    }
    ...
}
like image 221
Mohammad S. Avatar asked Sep 03 '17 09:09

Mohammad S.


1 Answers

services.AddTransient(typeof(IDataService<>), typeof(DataService<>));

Ideally this should not be allowed, but as method accepts type as parameter, it took it without performing any validation. As nobody expected anyone would try to use it.

The reason it is null, because typeof(IDataService<>) !== typeof(IDataService<SomeClass>)

You can check example at https://dotnetfiddle.net/8g9Bx7

That is the reason, DI resolver will never know how to resolve. Most DI containers resolve types only if type implements requested interface or has base class as requested class.

Any DI container will resolve type A for type B, only if A inherits B or A implements B.

In your case, DataService<> implements IDataService<>, but DataService<T> does not implement IDataService<>

Only way you can make it work is by calling same for every data type

services.AddTransient(typeof(IDataService<Customer>), typeof(DataService<Customer>));

services.AddTransient(typeof(IDataService<Order>), typeof(DataService<Order>));

services.AddTransient(typeof(IDataService<Message>), typeof(DataService<Message>));

OR

You can create a ServiceFactory...

interface IDataServiceFactory{
     DataService<T> Get<T>();
}

class DataServiceFactory : IDataServiceFactory{
     public DataService<T> Get<T>(){
          //.. your own logic of creating DataService

          return new DataService<T>();
     }
}

And register

services.AddTransient(typeof(IDataServiceFactory), typeof(DataServiceFactory));
like image 55
Akash Kava Avatar answered Oct 02 '22 16:10

Akash Kava