Repository Pattern with Entity Framework Core is obsolete
Many developers when building multi-tier, multi-layered application use patterns to enhance the testability and usability of their apps. They often use UoW and Repository patterns which in general are great patterns to use.
For example, in the case of the repository pattern, the purpose is to abstract away the low-level database query logic.
Developers used to write SQL statements in their code and the repository pattern was a way to move that SQL out of individual methods scattered throughout the code base.
Developers building applications with ASP.Net Core and Entity Framework Core should not use UoW and Repository pattern anymore. EF Core supports unit testing and mock contexts.
EF Core is 100% test-friendly, one can even mock what e.g SaveChanges method (returns the count of records that were affected) returns.
It also supports injecting the database context into the web request by using a service that uses Dependency Injection.
In a nutshell, Entity Framework already implements a repository pattern. DbContext is the UoW (Unit of Work) and each DbSet is the repository. By implementing another layer on top of this, is not only redundant, but makes maintenance much more harder.
Let's ask ourselves. What is DbContext? It is a class. This is a no-brainer. It is a class that contains multiple properties, each implementing IDbSet. IDbSet is the same thing as IRepository.
It contains methods to Add, Delete etc. It also implements IQueryable – so you have the whole LINQ query set including things like First, Single, Where.
The example below demonstrates how to add an Entity Framework Core database context as a service using Dependency Injection in an ASP.Net MVC Core 3.1 application. These are just snippets and not the whole codebase.
We will also demonstrate how to reference the database context from an MVC Controller.
We need to have a connection string e.g DefaultConnection. This is confugured in the appsettings.json, appsettings.Development.json files.
In this example the database server is SQL Server.
You also need to install Microsoft.EntityFrameworkCore.SqlServer Nuget package.
Model/ViewModel class:
public class Customer {
[Key]
public int Id { get; set; }
public String FullName { get; set; }
}
DBContext class:
public class ModelContext : DbContext {
public ModelContext(DbContextOptions<ModelContext> options) : base(options) {}
public DbSet<Customer> Customers { get; set; }
}
We now need to set up dependency injection by configuring the Startup.cs file:
public void ConfigureServices(IServiceCollection services) {
services.AddDbContext<ModelContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddControllersWithViews();
}
Now we need to make DBContext accessible from within each controller e.g CustomerController through the constructor. See below (just snippet)
public class CustomerController: Controller
{
private readonly ModelContext _context;
public CustomerController(ILogger<CustomerController> logger, ModelContext context)
{
_logger = logger;
_context = context;
}
public IActionResult Index() {
List<Customer> AllCustomers = _context.Customers.ToList();
return View(AllCustomers);
}
}
To recap, when writing code try to avoid needless complexity by over-engineering things.
Getting data from the database is a common operation and by adding additional layers of abstractions usually only makes it much harder to maintain the code.