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.
<code class="language-csharp" data-lang="csharp">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 }</code>
It is specific to Employee class, respository contains CRUD operations. And here is the implementation of EmployeeRepository class with DbContext.
<code class="language-csharp" data-lang="csharp"> 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 }</code>
And here is the Employee context object.
<code class="language-csharp" data-lang="csharp"> 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 }</code>
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.
<code class="language-csharp" data-lang="csharp">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 }</code>
The IEntity interface contains only one property, Id.
<code class="language-csharp" data-lang="csharp">1 public interface IEntity
2 {
3 Guid Id { get; set; }
4 }</code>
And here is the implementation of GenericRepository class.
<code class="language-csharp" data-lang="csharp"> 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 }</code>
In this implementation, one more problem exists, in the DbContext implementation, you need the reference of Employee model. You can make it DbContext also generic using Reflection.
<code class="language-csharp" data-lang="csharp"> 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 }</code>
In OnModelCreating method, all the types which implements IEntity interface are added to the DbContext using 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.
<code class="language-csharp" data-lang="csharp">1 public void ConfigureServices(IServiceCollection services)
2 {
3 services.AddMvc();
4 services.AddScoped<DbContext, GenericDbContext>();
5 services.AddScoped<IGenericRepository<Employee>, GenericRepository<Employee>>();
6 }</code>
And here is the unit tests for create
Happy Programming :)