EFCore Repository Implementation in ASP.NET Core
In this article, we will learn Entity Framework Repository Implementation for Relational databases like SQL in the .NET Core or ASP.NET Core 3.1 or .NET 6 application.
We already discussed basic details on getting started with EFCore in ASP.NET Core in our last article.
In Today’s article, we shall see how to extend the same existing implementation to use a better pattern i.e Repository.
Below are the steps which we shall be following to create a Repository,
- EFCore scaffolding as Repository and UoW
- Entity Framework DBContext as Repository and UOW
- Entity Framework- Create Models using Schema
- Entity Framework – using Database First
- Entity Framework- Define Repository Interfaces
- Entity Framework- Define Repository Class
- Using a Repository in API/Service
- Repository and DBContext using IoC Dependency injection Container
- Summary
EFCore scaffolding as Repository and UoW
EFCore is an ORM-Object-relational mapping framework that helps to represent the Database into the object-oriented programming model in the .NET Core ecosystem helping to interact and perform CRUD operation on relational DB without hassle.
Repository design patterns fit into any NoSQL or Relational DB requirements and also can be used for multiple other requirements.
Entity Framework DBContext as Repository and UOW
Using tools like Entity Framework gives us DBContext which already represents repository and UoW (Unit Of Work) implementation with very minimal effort.
The best thing is you can use DBContext anywhere from your code.
This DBContext can be DI (Dependency Injected ) from the API pipeline or Middleware as needed or can be consumed directly.
However, if you see DBContext is a class and not an Interface.
If you need to perform Test-Driven Development like Unit Testing, you will find a few challenges in mocking DBContext to simulate the Data access.
So considering testability as an important principle, one can easily implement a Repository.
Repository encourages a more loosely coupled approach to accessing our data from the database.
The code becomes cleaner and maintainable and highly extensible.
Entity Framework- Create Models using Schema
Do you follow schema modeling ??.
EFCore works very well for the Database First approach.
Where it lets you create the required scaffolding and domain entities using the existing Database.
However, You can follow the code-first approach as well followed by using Migration and Update database commands to create the database.
Entity Framework – using Database First
We shall be learning EFCore using Database first approach where we already have an existing Database called ‘Master‘ and a table name called Employee.
SQL Database Schema
We can start with the below simple Database schema,
I have discussed how to do ECore scaffolding and generate domain entities in the below article.
Once you are ready with scaffolding please use the repository to access DBContext objects as below.
Entity Framework- Define Repository Interfaces
Repository Interfaces will be defined as below,
public interface IEmployeeRepository
{
IEnumerable<Employee> GetEmployees();
Employee GetEmployeeByID(int employeeID);
void InsertEmployee(Employee employee);
void DeleteEmployee(int employeeID);
void UpdateEmployee(Employee employee);
}
We shall define repository class specific to the context, i.e. here EmployeeRepository will be dealing with Employee DBContext.
Entity Framework- Define Repository Class
Please see the implementation for the Repository class performing CRUD operation below.
EmployeeContext object is accessed using EmployeeRepository Constructor injections as below,
public class EmployeeRepository : IEmployeeRepository
{
private readonly EmployeeContext _context;
public EmployeeRepository(EmployeeContext context)
{
_context = context;
}
public IEnumerable<Employee> GetEmployees()
{
return _context.Employee.ToList();
}
public Employee GetEmployeeByID(int employeeID)
{
return _context.Employee.Find(employeeID.ToString());
}
public void InsertEmployee(Employee employee)
{
_context.Employee.Add(employee);
}
public void DeleteEmployee(int employeeID)
{
Employee employee = _context.Employee.Find(employeeID);
_context.Employee.Remove(employee);
}
public void UpdateEmployee(Employee employee)
{
_context.Entry(employee).State = EntityState.Modified;
}
}
EmployeeContext is created using database Scaffolding DBContext Commands as discussed in our Getting Started using EFCore article.
Now that our Repository is ready, this can be integrated into the code.
If following non-DDD or DDD architecture this repository will be responsible for performing CRUD and will also be used for data persistence through DBContext.
Using a Repository in API/Service
Here is an example of how I am using the repository in EmployeeController (In a similar way this repository can be interfaced from the Domain or Business layer as required (if you have any).
EmployeeRepository object is accessed using EmployeeController’s Constructor injections.
Below is a very simple and minimal implementation,
[Route("api/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
private readonly IEmployeeRepository _employeeRepository;
public EmployeeController(IEmployeeRepository employeeRepository)
{
_employeeRepository = employeeRepository;
}
// GET: api/Employee
[HttpGet]
public ActionResult<IEnumerable<Employee>> Get()
{
return Ok(_employeeRepository.GetEmployees());
}
// GET: api/Employee/5
[HttpGet("{id}")]
public ActionResult Get(int id)
{
return Ok(_employeeRepository.GetEmployeeByID(id));
}
// POST: api/Employee
[HttpPost]
public void Post([FromBody] Employee value)
{
_employeeRepository.InsertEmployee(value);
}
// DELETE: api/5
[HttpDelete("{id}")]
public void Delete(int employeeID)
{
_employeeRepository.DeleteEmployee(employeeID);
}
}
Repository and DBContext using IoC Dependency injection Container
Please initialize Repository and DBContext in the Service Container as below,
Dependency injection Container in .NET 3.1
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddScoped<IEmployeeRepository, EmployeeRepository>();
services.AddDbContext<EmployeeContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("EmployeeDB"),
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.EnableRetryOnFailure();
});
});
}
Dependency injection Container in .NET 6 and above
If using .NET 6 and above, please use builder instances to add the lifetime of required services,
Example
builder.services.AddScoped<IEmployeeRepository, EmployeeRepository>();
builder.services.AddDbContext<EmployeeContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("EmployeeDB"),
sqlServerOptionsAction: sqlOptions =>
{
sqlOptions.EnableRetryOnFailure();
});
});
Please note that the repository instance lifetime in your IoC container should be set as Scoped (same as DBContext) as a good practice.
As shown in the above code, we also have resiliency implemented for SQL Connection using EnableRetryOnFailure() added with the DBContext instance.
As a good practice, implement resiliency in SQL operation to address any issues related to Transient errors.
One can also implement a more Generic repository around DBContext if needed addressing multiple Domain models as discussed below article if needed.
You are all set to use a repository in your code.
Other References :
Do you have any comments or ideas or any better suggestions to share?
Please sound off your comments below.
Happy Coding !!
Summary
Today in this article we learned how to implement a Repository around SQL database using the EFCore ORM framework.
Application with a complex business/domain model gets a huge advantage over the Repository.
It provides an abstraction that not only isolates the business objects from the database access code but also provides a clean separation of concerns in an Object-oriented way.
Please bookmark this page and share it with your friends. Please Subscribe to the blog to receive notifications on freshly published(2024) best practices and guidelines for software design and development.
hi
where do you call saveChanges() ?
Hi Reza – Can you please elaborate your question ?
Hi
in EmployeeController
you used the IEmployeeRepository ,right ?
after creating or editing or removing actions
I cannot see the call to saveChanges method .
where do you call saveChanges() in the codebase
Thank you Reza for clarifying your question! You are right on _context.SaveChanges() method is not called after each operation but you can use it. Actually, I have an Action filter that calls the saveChanges after per request transaction which I have not shown in the sample code to avoid complexity of article.
Also calling savechanges for multiple db changes sometime makes it slow and unpredictable so you can give a try to this approach. You can refer for article on action filter adding globally here Filters in ASP.NET Core – Best Practices
Hope this helps
Tnx
The approach that you used is new to me
I will give it a try
Tnx again
You are welcome!
Nice work!
Thanks Yanhui, Glad you liked the article.
This was very helpful. Thank you !
Thanks Yola. Appreciate your feedback. Have a good one !