Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic repository in ASP.NET Core without having a separate AddScoped line per table in Startup.cs?

I have a generic repository in my project. Consider the following controller snippet

public class Lookup1Controller : Controller {     readonly MyDbContext _db;      public Lookup1Controller(MyDbContext dataContext)     {         _db = dataContext;     }      public async Task<IActionResult> Index()     {          IGenericRepository<Lookup1> _repository = new GenericRepository<Lookup1>(_db);         var lookup1s = await _repository.SelectAll();          return  View(lookup1s);     } 

I don't see the need to have my Database reference both in my Generic repository as well as each of my controllers.

I refactor it to:

public class Lookup1Controller : Controller {     private IGenericRepository<Lookup1> _repository;      public Lookup1Controller(IGenericRepository<Lookup1> repository)     {         _repository = repository;     }      public async Task<IActionResult> Index()     {         var lookup1s = await _repository.SelectAll();          return  View(lookup1s);     }  } 

which is much neater and ASP.NET 5 best practice from what I read. but I will get the following error if I access that controller route in my browser:

InvalidOperationException: Unable to resolve service for type 'MyProject.Data.IGenericRepository`1[MyProject.Models.Lookup1]' while attempting to activate 'MyProject.Controllers.Lookup1. 

because of I haven't injected the GenericRepository to use the interface.

I add to my Startup.cs an AddScoped line for each and every of my tables in the ConfigureServices method

services.AddScoped<IGenericRepository<Lookup1>,GenericRepository<Lookup1>> (); services.AddScoped<IGenericRepository<Lookup2>,GenericRepository<Lookup2>> (); services.AddScoped<IGenericRepository<Lookup3>,GenericRepository<Lookup3>> (); services.AddScoped<IGenericRepository<Lookup4>,GenericRepository<Lookup4>> (); etc 

so that my code runs without throwing an exception.

However my database has about 100 simple lookup tables. When I look at the above 100 lines of code it just doesn't look right.

It feels like copy and paste code. Each time I add a new table by adding a new model and controller with view my code will compile without giving me an error. But if I run the program and go to that view I could get the controller run error if I forgot to add the AddScoped line to my Startup.cs. Not really good for maintainability.

My question:

  1. Is it really best practice to have a services.AddScoped for each and every lookup table in the ConfigureServices method of Startup.cs?

  2. It is a generic repository so isn't there be a way to write those 100 copy and paste lines in one line?

  3. If not then what is the best practice way to do this using my code?

like image 459
dfmetro Avatar asked Nov 06 '15 11:11

dfmetro


People also ask

How do I register a generic repository in .NET Core?

You have to register the dependencies in IServiceCollection in startup class. ASP.NET Core supports the constructor Injections to resolve the injected dependencies. Here, register the interface and their implementation into DI, using the add method of different lifetimes.

What is generic repository pattern C#?

The generic repository pattern implements in a separate class library project. It uses the "Code First" development approach and creates a database from a model, using migration. This article demonstrates a sample Application, which has one too many relationships in ASP.NET Core with Entity Framework Core.

What is the use of repository pattern in ASP.NET Core?

Repository Pattern is an abstraction of the Data Access Layer. It hides the details of how exactly the data is saved or retrieved from the underlying data source. The details of how the data is stored and retrieved is in the respective repository.

What is scoped dependency?

For scoped dependencies, the activator creates one instance per web request. Within that request, every consumer shares that single instance, but from request to request, the instances are different. For transient dependencies, each consumer gets its own private instance. There is no sharing at all.


2 Answers

Just use the non-generic registration overloads (the ones where you need to pass the 2 Type objects.) Then provide the open generic types of both your interface and the implementation:

services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>)); 

In your controller, add a dependency for a repository of a specific type (a closed generic type):

public HomeController(IGenericRepository<Lookup1> repository) {     ... } 
like image 196
Daniel J.G. Avatar answered Sep 28 '22 21:09

Daniel J.G.


If you would like to register all IGenericRepository<> implementations in Assembly:

services.AddAllGenericTypes(typeof(IGenericRepository<>), new[] {typeof(MyDbContext).GetTypeInfo().Assembly}); 

With extension from: https://gist.github.com/GetoXs/5caf0d8cfe6faa8a855c3ccef7c5a541

like image 42
GetoX Avatar answered Sep 28 '22 20:09

GetoX