Monday, August 31, 2020

EFCore Repository Implementation in ASP.NET Core

 

In this article, we will learn how to create a Repository pattern for Relational databases like SQL in the .NET Core or ASP.NET Core 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 better pattern i.e Repository.

Below are the steps which we shall be following to create a Repository,

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 performing 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 EFCore 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 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 important principles, one can easily implement Repository.

Repository encourages a more loosely coupled approach to access our data from the database. The code becomes cleaner and maintainable and highly extensible.

Create Models using Schema

Do you follow schema modeling??.

EFCore works very well for 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.

EFCore 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.

Defining Repository Interfaces

Repository Interfaces will be defined as below,

1
2
3
4
5
6
7
8
9
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.

Define Repository Class

Please see below the implementation for the Repository class performing CRUD operation as below.

EmployeeContext object is accessed using EmployeeRepository Constructor injections as below,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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 since 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 (Similar way this repository can be interfaced from Domain or Business layer as required (if you have any).

EmployeeRepository object is accessed using EmployeeController’s Constructor injections.

Below is very simple and minimal implementation,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[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 in IoC Container

Please initialize Repository and DBContext in the Service Container as below,

1
2
3
4
5
6
7
8
9
10
11
12
13
public void ConfigureServices(IServiceCollection services)
       {
           services.AddControllers();
           services.AddScoped<IEmployeeRepository, EmployeeRepository>();
           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 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 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 in the below article if needed.

You are all set to use a repository into your code.

That’s all! Happy Coding!

Other References :

Do you see any improvements to the above code? Please sounds off your comments below.

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 from 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.

Thank you for reading. Please let me know your questions, thoughts or feedback below in the comments section. I appreciate your feedback and encouragement.

Please share this article with your friends and Subscribe to the blog to get a notification on freshly published best practices of software development.


No comments:

Post a Comment