Thứ Sáu, 27 tháng 11, 2015

Generic Repository in ASPNET5

In this post, I am explaining generic repository pattern using EF7. The Repository Pattern is a common construct to avoid duplication of data access logic throughout our application. The purpose of the repository is to hide the details of accessing the data. We can easily query the repository for data objects, without having to know how to provide things like a connection string. The Repository pattern adds a layer between the data and domain layers of an application. It also makes the data access parts of an application better testable.
Here is the initial version of repository interface.
1 public interface IEmployeeRepository
2 {
3     Task<Employee> Get(Guid? id);
4     Task Save(Employee employee);
5     Task Delete(Employee employee);
6     Task Update(Employee employee);
7     Task<IEnumerable<Employee>> FindAll();
8 }
It is specific to Employee class, respository contains CRUD operations. And here is the implementation ofEmployeeRepository class with DbContext.
 1 public class EmployeeRepository : IEmployeeRepository
 2 {
 3     private EmployeeContext _employeeContext;
 4     public EmployeeRepository()
 5     {
 6         _employeeContext = new EmployeeContext();
 7     }
 8     public async Task<Employee> Get(Guid? id)
 9     {
10         return await _employeeContext.Employees.FirstOrDefaultAsync(x => x.Id == id);
11     }
12     
13     public async Task Save(Employee employee)
14     {
15         _employeeContext.Employees.Add(employee);
16         await _employeeContext.SaveChangesAsync();
17     }
18     
19     public async Task Delete(Employee employee)
20     {
21         _employeeContext.Employees.Remove(employee);
22         await _employeeContext.SaveChangesAsync();
23     }
24     
25     public async Task Update(Employee employee)
26     {
27         _employeeContext.Employees.Update(employee);
28         await _employeeContext.SaveChangesAsync();
29     }
30     
31     public async Task<IEnumerable<Employee>> FindAll()
32     {
33         return await _employeeContext.Employees.ToListAsync();
34     }
35 }
And here is the Employee context object.
 1 public class EmployeeContext : DbContext
 2 {
 3     private static bool _created = false;
 4 
 5     public EmployeeContext()
 6     {
 7         if (!_created)
 8         {
 9             Database.EnsureCreated();
10             _created = true;
11         }
12     }
13     
14     public DbSet<Employee> Employees { get; set; }
15     protected override void OnConfiguring(EntityOptionsBuilder optionsBuilder)
16     {
17         optionsBuilder.UseInMemoryStore();
18     }
19 }
There are two problems with current EmployeeRepository implementation. First one it is using one model class, Employee, if you have multiple model classes, you need to duplicate lot of code. Second is it is not testable. The first problem you can fix by make it generic. And the second problem you can resolve by injecting the context object. Here is the generic repository interface.
1 public interface IGenericRepository<T> where T: class, IEntity, new()
2 {
3     Task<T> Get(Guid? id);
4     Task Save(T employee);
5     Task Delete(T employee);
6     Task Update(T employee);
7     Task<IEnumerable<T>> FindAll();
8 }
The IEntity interface contains only one property, Id.
1 public interface IEntity
2 {
3     Guid Id { get; set; }
4 }
And here is the implementation of GenericRepository class.
 1 public class GenericRepository<T> : IGenericRepository<T> where T: class, IEntity, new()
 2 {
 3     private DbContext _dbContext;
 4     public GenericRepository(DbContext dbContext)
 5     {
 6         _dbContext = dbContext;
 7     }
 8 
 9     public async Task Delete(T employee)
10     {
11         _dbContext.Set<T>().Remove(employee);
12         await _dbContext.SaveChangesAsync();
13     }
14 
15     public async Task<IEnumerable<T>> FindAll()
16     {
17         return await _dbContext.Set<T>().ToListAsync();
18     }
19 
20     public async Task<T> Get(Guid? id)
21     {
22         return await _dbContext.Set<T>().FirstOrDefaultAsync(x => x.Id == id);
23     }
24 
25     public async Task Save(T employee)
26     {
27         _dbContext.Set<T>().Add(employee);
28         await _dbContext.SaveChangesAsync();
29     }
30 
31     public async Task Update(T employee)
32     {
33         _dbContext.Set<T>().Update(employee);
34         await _dbContext.SaveChangesAsync();
35     }
36 }
In this implementation, one more problem exists, in the DbContext implementation, you need the reference ofEmployee model. You can make it DbContext also generic using Reflection.
 1 public class GenericDbContext : DbContext
 2 {
 3     private static bool _created = false;
 4     public GenericDbContext()
 5     {
 6         if (!_created)
 7         {
 8             Database.EnsureCreated();
 9             _created = true;
10         }
11     }
12     protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
13     {
14         optionsBuilder.UseInMemoryDatabase(true);
15     }
16     protected override void OnModelCreating(ModelBuilder modelBuilder)
17     {
18         var types = Assembly.GetExecutingAssembly().GetTypes()
19             .Where(type => typeof(IEntity).IsAssignableFrom(type) && type.IsClass);
20         var method = typeof(ModelBuilder).GetMethods().First(m => m.Name == "Entity"
21             && m.IsGenericMethodDefinition
22             && m.GetParameters().Length == 0);
23         foreach (var type in types)
24         {
25             method = method.MakeGenericMethod(type);
26             method.Invoke(modelBuilder, null);
27         }
28 
29         base.OnModelCreating(modelBuilder);
30     }
31 }
In OnModelCreating method, all the types which implements IEntity interface are added to the DbContextusing Entity() method. This method is invoked dynamically using reflection. In ASP.NET 5, you can inject the repository and the context using inbuilt dependency injection feature.
1 public void ConfigureServices(IServiceCollection services)
2 {
3     services.AddMvc();
4     services.AddScoped<DbContext, GenericDbContext>();
5     services.AddScoped<IGenericRepository<Employee>, GenericRepository<Employee>>();
6 }
And here are the unit tests for create.
Happy programming! Smile | :)

Source: http://www.codeproject.com/Articles/1035618/Generic-Repository-in-ASPNET

Không có nhận xét nào:

Đăng nhận xét