In this article you will learn how to use the repository pattern for CRUD operations and how to combine it with the unit of work patterns. Before going to write the code, let's understand the repository and unit of work patterns separately.
Repository Pattern
The repository pattern is used to create an abstraction layer between the DAL (data access layer) and the BAL (business access layer) to perform CRUD operations.
Repository Pattern Implementation
The repository pattern can be implemented by using following two method :
Generic Repository Pattern
A generic repository implementation is used to define common database operations (like Create, Retrieve Update, Delete etc.) for all the database entities in a single class.- public interface IRepository
where TEntity : class { IEnumerable GetAll(); IEnumerable Find(Expression > predicate); TEntity Get(object Id); void Add(TEntity entity); void AddRange(IEnumerable entities); void Update(TEntity entity); void Remove(object Id); void Remove(TEntity entity); void RemoveRange(IEnumerable entities); } // implementation public class Repository : IRepository where TEntity : class { protected readonly DbContext db; public Repository(DbContext _db) { db = _db; } public IEnumerable GetAll() { return db.Set ().ToList(); } public IEnumerable Find(Expression > predicate) { return db.Set ().Where(predicate); } public TEntity Get(object Id) { return db.Set ().Find(Id); } public void Add(TEntity entity) { db.Set ().Add(entity); } public void AddRange(IEnumerable entities) { db.Set ().AddRange(entities); } public void Remove(TEntity entity) { db.Set ().Remove(entity); } public void RemoveRange(IEnumerable entities) { db.Set ().RemoveRange(entities); } public void Remove(object Id) { TEntity entity = db.Set ().Find(Id); this.Remove(entity); } public void Update(TEntity entity) { db.Entry (entity).State = EntityState.Modified; } }
- public interface IRepository
Non-Generic Repository Pattern (Specific Repository)
A non-generic repository implementation is used to define all database operation related to an entity within a separate class. For example, if you have two entities Category and Product, each entity will have its own implementation repository.- public interface IProductRepository
- {
- IEnumerable
GetAll(); IEnumerable Find(Expression > predicate); Product Get(int Id); void Add(Product entity); void AddRange(IEnumerable entities); void Update(Product entity); void Remove(int Id); void Remove(Product entity); void RemoveRange(IEnumerable entities); } // implementation public class ProductRepository { protected readonly DbContext db; public Repository(DbContext _db) { db = _db; } public IEnumerable GetAll() { return db.Products.ToList(); } public IEnumerable Find(Expression > predicate) { return db.Products.Where(predicate); } public Product Get(int Id) { return db.Products.Find(Id); } public void Add(Product entity) { db.Products.Add(Product); } public void AddRange(IEnumerable entities) { db.Products.AddRange(entities); } public void Remove(Product entity) { db.Products.Remove(entity); } public void RemoveRange(IEnumerable entities) { db.Products.RemoveRange(entities); } public void Remove(object Id) { Product entity = db.Products.Find(Id); this.Remove(entity); } public void Update(Product entity) { db.Entry(entity).State = EntityState.Modified; } }
Recommended Repository Pattern Implementation
If you will use one of the above implementation, generic you can not use for specific operation on an entity and in case of non-generic implementation, you have to write code for common CRUD operations for each entity. So better way is, just create a generic repository for commonly used CRUD operation and for specific one create a non-generic repository and inherit form generic repository. The example code is given below:
- public interface IProductRepository : IRepository
{ IEnumerable GetProductsByCategory(int id); } public class ProductRepository : Repository , IProductRepository { public DatabaseContext context { get { return db as DatabaseContext; } } public ProductRepository(DatabaseContext _db) : base(_db) { } public IEnumerable GetProductsByCategory(int id) { return context.Products.Where(p => p.CategoryId == id).ToList(); } }
Unit of work Pattern
The unit of pattern implementation manage in-memory database CRUD operations on entities as one transaction. So, if one of the operation is failing then entire db operations will be rollback.
- public interface IUnitOfWork : IDisposable
- {
- ICategoryRepository Categories { get; }
- IProductRepository Products { get; }
- int SaveChanges();
- }
- public class UnitOfWork : IUnitOfWork
- {
- private readonly DatabaseContext db;
- public UnitOfWork()
- {
- db = new DatabaseContext();
- }
- private ICategoryRepository _Categories;
- public ICategoryRepository Categories
- {
- get
- {
- if (this._Categories == null)
- {
- this._Categories = new CategoryRepository(db);
- }
- return this._Categories;
- }
- }
- private IProductRepository _Products;
- public IProductRepository Products
- {
- get
- {
- if (this._Products == null)
- {
- this._Products = new ProductRepository(db);
- }
- return this._Products;
- }
- }
- public int SaveChanges()
- {
- return db.SaveChanges();
- }
- public void Dispose()
- {
- db.Dispose();
- }
- }
Entity Framework and Repository and Unit Of Work Patterns
Entity Framework is based on the repository and unit of work patterns to perform CRUD operations on an entity.
The DbSet class is based on the repository design pattern which provides us a set of method to perform CRUD operations on an entity. The DbContext class is based on unit of work pattern which includes all the DbSet entities. The DbContext class manages in-memory database operations on these entities and later saves all these updates as one transaction into database.
Advantages of Repository and Unit Of Work Design Patterns
- Abstract Data Access Layer and Business Access Layer from the Application.
- Manage in-memory database operations and later saves in-memory updates as one transaction into database.
- Facilitates to make the layers loosely-coupled using dependency injection.
- Facilitates to follow unit testing or test-driven development (TDD).
What do you think?
I would like to have feedback from my blog readers. Your valuable feedback, question, or comments about this article are always welcome.