Click here to Skip to main content
15,881,139 members
Articles / Web Development / ASP.NET
Technical Blog

Generic Repository in ASPNET5

Rate me:
Please Sign up or sign in to vote.
4.33/5 (4 votes)
3 Oct 2015MIT1 min read 13.1K   10   5
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.

C#
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 of EmployeeRepository class with DbContext.

C#
 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.

C#
 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.

C#
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.

C#
1 public interface IEntity
2 {
3     Guid Id { get; set; }
4 }

And here is the implementation of GenericRepository class.

C#
 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 of Employee model. You can make it DbContext also generic using Reflection.

C#
 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 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.

C#
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! :)

This article was originally posted at http://dotnetthoughts.net/Generic-repository-in-aspnet5

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Technical Lead
India India
Working as Tech. Lead

My blog : dotnetthoughts.net.
You can follow me in twitter : @anuraj

Comments and Discussions

 
QuestionDependency injection ASP.NET 5 BETA8 Pin
marcosph13-Nov-15 7:22
marcosph13-Nov-15 7:22 
QuestionFeedback Pin
Randall Eike10-Oct-15 11:13
Randall Eike10-Oct-15 11:13 
AnswerRe: Feedback Pin
Anuraj Parameswaran11-Oct-15 15:13
Anuraj Parameswaran11-Oct-15 15:13 
GeneralMy vote of 4 Pin
Santhakumar M5-Oct-15 18:17
professionalSanthakumar M5-Oct-15 18:17 
GeneralRe: My vote of 4 Pin
Anuraj Parameswaran11-Oct-15 15:14
Anuraj Parameswaran11-Oct-15 15:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.