Dependency injection for generic class - c#

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();
...
}
...
}

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));

Microsoft's Logging extension uses open generics in their service collection configuration
services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>)));
That line lets the closed version of the logger class be injected as
public SomeService(ILogger<SomeService> logger)
{
// ...
Testing shows that, for a shared service scope, each different type of T will map to a different instance of the service, and that any valid type T that can be requested
If you want to play around with this, use this testbed: https://dotnetfiddle.net/dVDqnj
This approach assumes that behavior is not defined for specific implementations of the interface, but is more informational. If you need different implementations for IGeneric<Foo> and IGeneric<Bar>, you'll need to define each implementing service individually.

Related

Autofac - How to register a class<,> to interface<>

I'm trying to register my repository class which is take two generic parameter to repository interface (one parameter generic).
public class RepositoryBase<T, O> : IDataAccess<T>
public interface IDataAccess<T>
Autofac don't know how to set the second generic parameter of RepositoryBase when you only want to provide one generic argument in the interface. So you need to somehow specify the second argument in order to instantiate the service unless you are happy with object, dynamic or some base class. In order to achieve this you can create a factory like this:
public class BaseRepositoryFactory<T1> : IDataAccessFactory<T1>
{
private readonly IComponentContext context;
public BaseRepositoryFactory(IComponentContext context)
{
this.context = context;
}
public IDataAccess<T1> Create<T2>()
{
return context.Resolve<IRepositoryBase<T1, T2>>();
}
}
public interface IDataAccessFactory<T1>
{
IDataAccess<T1> Create<T>();
}
public interface IRepositoryBase<T1, T2> : IDataAccess<T1>
{
}
public class RepositoryBase<T1, T2> : IDataAccess<T1>, IRepositoryBase<T1, T2>
{
}
And then register the services like:
builder.RegisterGeneric(typeof(BaseRepositoryFactory<>))
.As(typeof(IDataAccessFactory<>));
builder.RegisterGeneric(typeof(RepositoryBase<,>))
.As(typeof(IRepositoryBase<,>));
After that you can resolve the factory and create service;
var factory = c.Resolve<IDataAccessFactory<string>>();
IDataAccess<string> serviceInterface = factory.Create<int>();
var serviceConcrete = (RepositoryBase<string, int>)factory.Create<int>();
So this way you can move the declaration of a second argument to a factory method. The drawback of this is that you need to create such factories for each type. To avoid this you can define additional interface with two generic arugments IDataAccess<T1, T2> and implement it in concrete class and register it so your factory can look like this and will work for any type:
public class DataAccessFactory<T1> : IDataAccessFactory<T1>
{
private readonly IComponentContext context;
public DataAccessFactory(IComponentContext context)
{
this.context = context;
}
public IDataAccess<T1> Create<T2>()
{
return context.Resolve<IDataAccess<T1, T2>>();
}
}
Try this:
builder.RegisterGeneric(typeof(RepositoryBase<,>))
.As(typeof(IDataAccess<>))
.InstancePerRequest();

How to inject dependencies of generics in ASP.NET Core

I have following repository classes:
public class TestRepository : Repository<Test>
{
private TestContext _context;
public TestRepository(TestContext context) : base(context)
{
_context = context;
}
}
public abstract class Repository<T> : IRepository<T> where T : Entity
{
private TestContext _context;
public Repository(TestContext context)
{
_context = context;
}
...
}
public interface IRepository<T>
{
...
}
How do I implement the dependency injection in ASP.NET Core in my Startup.cs?
I implemented it like this:
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
But then I get following error:
Cannot instantiate implementation type 'Test.Domain.Repository1[T]'
for service type 'Test.Domain.IRepository1[T]'.
Repository<T> is an abstract class, so you cannot register it as an implementation, because abstract class simply cannot be instantiated. Your registration would work fine if Repository<T> was not abstract.
If you cannot make repository class non-abstract, you can register specific implementation of your repository class:
services.AddScoped(typeof(IRepository<Test>), typeof(TestRepository));
This will correctly inject dependencies to your controller.
I know this is very late but I am posting my solution here so that others can refer and use this. I have written some extensions to register all the derived types of generic interface.
public static List<TypeInfo> GetTypesAssignableTo(this Assembly assembly, Type compareType)
{
var typeInfoList = assembly.DefinedTypes.Where(x => x.IsClass
&& !x.IsAbstract
&& x != compareType
&& x.GetInterfaces()
.Any(i => i.IsGenericType
&& i.GetGenericTypeDefinition() == compareType))?.ToList();
return typeInfoList;
}
public static void AddClassesAsImplementedInterface(
this IServiceCollection services,
Assembly assembly,
Type compareType,
ServiceLifetime lifetime = ServiceLifetime.Scoped)
{
assembly.GetTypesAssignableTo(compareType).ForEach((type) =>
{
foreach (var implementedInterface in type.ImplementedInterfaces)
{
switch (lifetime)
{
case ServiceLifetime.Scoped:
services.AddScoped(implementedInterface, type);
break;
case ServiceLifetime.Singleton:
services.AddSingleton(implementedInterface, type);
break;
case ServiceLifetime.Transient:
services.AddTransient(implementedInterface, type);
break;
}
}
});
}
In the startup class, you just register your generic interface like below.
services.AddClassesAsImplementedInterface(Assembly.GetEntryAssembly(), typeof(IRepository<>));
You can find the complete extension code in this Github repository.
You can register an abstract class only as a service, not as implementation as #dotnetom said.
See the definitions below
public abstract class BaseClass<T>
{
public BaseClass()
{
}
}
public class DerivedClass : BaseClass<Entity>
{
public DerivedClass() : base() { }
}
public class DerivedClass2<T> : BaseClass<T> where T: Entity
{
}
public class Entity
{
}
Controller:
public class WeatherForecastController : ControllerBase
{
......
public WeatherForecastController(BaseClass<Entity> baseClass)
{
}
...
}
If you inject them on this way:
services.AddScoped(typeof(BaseClass<>), typeof(DerivedClass));
you get this error when you are resolving your dependency:
Open generic service type 'BaseClass`1[T]' requires registering an open generic implementation type. (Parameter 'descriptors')
You should register the generic type or register the service and the implementation with the generic argument defined
Now this should work perfectly as well
services.AddScoped(typeof(BaseClass<>), typeof(DerivedClass2<>));
or
services.AddScoped(typeof(BaseClass<Entity>), typeof(DerivedClass2<Entity>));
IMHO I prefer to go with interface definitions rather than abstract classes.

Assigning implementation of a generic interface to a property

I have an interface
public interface IStrategy<T> where T : BaseModel
{
T GetModel(Guid userId);
}
and a concrete class inheriting the interface specifying that it should be a ConcreteModel
public class ConcreteStrategy: IStrategy<ConcreteModel>
{
ConcreteModel GetModel(Guid userId) { ... }
}
Now in the following method I can pass a new instance of ConcreteStrategy and everything works
public class Manager
{
public TModel GetContentModel<TModel>(IStrategy<TModel> strategy, Guid userId)
where TModel : ModelBase
{
return strategy.GetContentModel(userId);
}
}
But if I try to assign it to a property like this I get an error
public class Strategies
{
public static IStrategy<ModelBase> MyStrategy { get; set; }
}
Strategies.MyStrategy = new ConcreteStrategy();
Is there a way I can achieve this in C# ?
I want to be able to make a factory method that encapsulates the logic for which strategy to use and just return an instance of some type of strategy class (like ConcreteStrategy).
The error I am getting is:
Cannot implicitly convert type IStrategy<ModelBase> to IStrategy<ConcreteModel>
You need to make your interface covariant:
public interface IStrategy<out T> where T : BaseModel
Note that it will work only if T only appears in an output position in the interface (which is the case in the code you have shown, but I don't know if it's your real code).

Repository pattern with generics and DI

I have a base repository contract which other contracts extend, like follows
public interface IBaseRepository<T> where T : class
{
IList<T> GetContents();
}
and then there are other contracts which extend it like follows
public interface IRepository1 : IBaseRepository<MyClass1>
{
}
public interface IRepository2 : IBaseRepository<MyClass2>
{
}
I implement IRepository1 as follows
public class Repository1 : IRepository1
{
public IList<MyClass1> GetContents()
{
//some code goes here
}
}
similarly for IRepository2
public class Repository2 : IRepository2
{
public IList<MyClass2> GetContents()
{
//some code goes here
}
}
Now i have a service Service1 which implments IService like follows
public class Service1 : IService
{
}
I want to use my base repository (IBaseRepository) here in my service constructor, get an instance of this base repository and use it like so
public class Service1 : IService
{
private IBaseRepository<T> _baseRepository;
public Service1(IBaseRepository<T> baseRepository)
{
_baseRepository = baseRepository;
}
public MyMethod1()
{
var contentsOfType1 = _baseRepository<MyClass1>.GetContents();
}
public MyMethod1()
{
var contentsOfType2 = _baseRepository<MyClass2>.GetContents();
}
}
and this is what i am unable to do.
So i have a generic base repository contract with type T and have other contracts (interfaces) extending the base contract and also specifying what type T will be.
All these contracts (which extend generic base contract) have thier individual implementations.
What i want to do is in my service class, instantiate this generic base contract, and use it to infer the extending types (and hence implementations) and use the method from the base repository.
So if the base contract is
IBaseRepository<T>
and extending contract is
IRepository1 : IBaseRepository<MyClass1>
which is implemented by
Repository1 : IRepository1
i want to use this in my service class like
public class service()
{
*private IBaseRepository<T> _repo;
public service(IBaseRepository<T> repo)
{
*_repo = repo;
}
public void MyMethod()
{
*var x = _repo<MyClass1>.MethodFromIBaseRepository()
}
}
So its the *marked lines i want to achieve, which i am unable to.
I am using castle windsor for DI.
Thanks for your help guys
You should not have other repository interfaces besides your generic IRepository<T>. If you need those, you are missing an abstraction.
For instance, a common reason for people to have custom repository interfaces is because they have a custom query that some repository has, while other don't. For instance:
public interface IEmployeeRepository : IRepository<Employee>
{
Employee GetEmployeeOfTheMonth(int month);
}
The problem here is that the IEmployeeRepository is abused for a 'custom query'. Custom queries deserve their own (generic) abstraction:
// Defines a query
public interface IQuery<TResult>
{
}
// Defines the handler that will execute queries
public interface IQueryHandler<TQuery, TResult>
where TQuery : IQuery<TResult>
{
TResult Handle(TQuery query);
}
With this abstraction we can add custom queries to the system, without the need of creating IRepository<T> derivatives:
public class GetEmployeeOfTheMonthQuery : IQuery<Employee>
{
[Range(1, 12)]
public int Month { get; set; }
}
class GetEmployeeOfTheMonthHandler : IQueryHandler<GetEmployeeOfTheMonthQuery, Employee>
{
public Employee Handle(GetEmployeeOfTheMonthQuery query)
{
// todo: query the database, web service, disk, what ever.
}
}
A consumer that needs to know the employee of the month, can now simply take a dependency on IQueryHandler<GetEmployeeOfTheMonthQuery, Employee> and execute the query as follows:
var query = new GetEmployeeOfTheMonthQuery { Month = 11 };
var employee = this.employeeOfMonthHandler.Handle(query);
This might seem like overhead, but this model is very flexible, scalable, and has many interesting benefits. For instance, it is very easy to add cross-cutting concerns by wrapping handlers with decorators.
This also allows our reposities to be hidden behind one generic interface, which allows us to easily batch register them at once and add decorators to them as well.
For more in depth information, read this article: Meanwhile… on the query side of my architecture.
Not possible. The inversion of control container provide dependencies through the constructor, hence it must know in the constructor what type you want to get. So you should do this:
public class service()
{
private IBaseRepository<MyClass1> _repo;
public service(IBaseRepository<MyClass1> repo)
{
_repo = repo;
}
public void MyMethod()
{
var x = _repo.MethodFromIBaseRepository()
}
}

How to bind generic types with constraints

I'm using Ninject DI container. And I've got the two
public interface IRepository<T> where T : AbstractEntity<T>, IAggregateRoot
{
// methods signatures
}
public class Repository<T> : IRepository<T> where T : AbstractEntity<T>, IAggregateRoot
{
// implementations
}
Then I'm trying to bind them in a separate module
public class DataAccessModule : Ninject.Modules.NinjectModule
{
public override void Load()
{
this.Bind<IRepository<>>().To<Repository<>>();
}
}
where this.Bind<IRepository<>>().To<Repository<>>(); is not recognized as a statement.
How do I make a bind?
Snagged this piece from here. Looks like it worked for them:
Bind(typeof(IRepository<>)).To(typeof(Repository<>));

Categories

Resources