18

Working on a read-only api service and making use of generics to package the operation into convention based process.

Repository interface:

public interface IRepository<TIdType,TEntityType> where TEntityType:class {
   Task<EntityMetadata<TIdType>> GetMetaAsync();
}

Repository implementation:

public class Repository<TIdType,TEntityType> : IRepository<TIdType,TEntityType> where TEntityType:class {
   public Repository(string connectionString) { // initialization }
   public async Tas<EntityMetadata<TIdType>> GetMetaAsync() { // implementation   }
}

In Startup.cs -> ConfigureServices :

services.AddSingleton<IRepository<int, Employee>> ( p=> new Repository<int, Employee>(connectionString));
services.AddSingleton<IRepository<int, Department>> ( p=> new Repository<int, Department>(connectionString));
// and so on

Controller:

public class EmployeeController : Controller {
   public EmployeeController(IRepository<int,Employee> repo) {//stuff}
}

I am currently repeating the repository implmentation for all types of entity types in the ConfigureServices. Is there a way to make this generic too?

services.AddSingleton<IRepository<TIdType, TEntityType>> ( p=> new Repository<TIdType, TEntityType>(connectionString));

so in the controller constructor call can automatically get the relevant repository?

Update 1: Not a duplicate:

  1. The repository implementation does not have default constructor
  2. Because it does not have default constructor, I cannot provide the solution given in the linked question.
  3. When trying services.AddScoped(typeof(IRepository<>), ...) I am getting error Using the generic type 'IRepostiory<TIdType,TEntityType>' requires 2 type arguments
Community
  • 1
  • 1
Vijay
  • 578
  • 2
  • 5
  • 15
  • http://stackoverflow.com/questions/33566075/generic-repository-in-asp-net-core-without-having-a-separate-addscoped-line-per – zaitsman Mar 29 '17 at 08:18
  • 3
    technically it is a duplicate. But when you have 2 parameters you have to use `typeof(IRepository<,>)` instead of `typeof(IRepository<>)` because it has two generic parameters – Tseng Mar 29 '17 at 08:52
  • @Tseng And could you also mention how to initiate the constructor of the repository? Maybe add it as an answer and I can mark it as complete. – Vijay Mar 29 '17 at 10:02
  • What do you mean with initiate? Any parameters in the constructor of the concrete implementation (i.e. `GenericRepository` will be resolved by the IoC container. In the services which require it, you request it via `IRepository` – Tseng Mar 29 '17 at 10:25
  • @Tseng kindly go through the full question – Vijay Mar 29 '17 at 10:29
  • I can't test it right now, but it am **pretty certain**, that your repository doesn't need a parameterless constructor neither do the dereived classes. I used that all the time for CQRS. But int more recent times I switched to Autofac for several other reasons – Tseng Mar 29 '17 at 11:16
  • As explained in the question repository accepts the connection string in the constructor. So I cannot use the syntax `typeof(IRepository<,>),typeof(Repository<,>)` as the second part requires me to pass the connection string. My question is how will I add this dependency during the service initialization as my repository **does not** have default constructor. – Vijay Mar 29 '17 at 11:23

1 Answers1

36

Since this question is still not properly marked as duplicate: The way to register a Generic class:

services.AddScoped(typeof(IRepository<,>), typeof(Repository<,>));

now you can resolve it in the following way:

serviceProvider.GetService(typeof(IRepository<A,B>));
// or: with extensionmethod
serviceProvider.GetService<IRepository<A,B>>();
Community
  • 1
  • 1
Joel Harkes
  • 10,975
  • 3
  • 46
  • 65
  • 4
    Thanks for answering, but to clarify the repository does not have default constructor as it needs to be initiated with the connection string. Currently I am registering the dependency as `services.AddSingleton> ( p=> new Repository(connectionString));` but how can I pass the connection string when using `services.AddScoped(typeof(IRepository<,>), typeof(Repository<,>));` – Vijay Mar 29 '17 at 13:57
  • @Vijay Use the IOptions Pattern, this way you can inject a typed class/options, strings wont work as you say you yave to manualy define strings in constructor – Joel Harkes Mar 29 '17 at 13:58
  • 1
    do you mean that my repository will have a dependency on a `MyConnectionStringOptions` type, which means I register that in the DI with an instance populated with proper connection strings. So that the constructor of the repository can use `GetService` the connection string options object and proceed? – Vijay Mar 29 '17 at 14:03
  • Exactly, i would call it `DatabaseOptions` though and also add `Timeout` and maybe some other usefull optoins and make it populate from Appsettings using the `IOptions` pattern and the `ConfigurationBuilder` – Joel Harkes Mar 29 '17 at 14:14
  • @Vijay What about the bit about your registrations being singletons? Or is that not relevant to you anymore? Unless this answer is somehow creating a singleton in a way that I can't see... – Ash Mar 12 '19 at 02:58
  • @Ash for singleton requirements we can make use of the `services.AddSingleton` registration. My query was how to get the generic repository without default constructor registered. – Vijay Apr 01 '19 at 18:58