DEV Community

loading...

Implement Repository Base and Unit of Work in C#

1001binary profile image 1001binary ・2 min read

Introduction and Implementation

The repository pattern belongs to the family of design patterns. It's used for handling common data access. It's easily maintainable and highly testable. Besides that, Unit Of Work can be seen as transaction to ensure that all operations in an unit are successfully executed. If one of them is failed, nothing happens.

In C#, there is an interface called IQueryable. It is like lazy interface. When called, it will be executed. This interface fits to Repository pattern well.

First, we define a new class EntityBase with Id and Version.

public class EntityBase
{
   public int Id { get; set; }
   public byte[] Version { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

The Version property can help to check concurrency in database. Nowadays, most of ORMs support concurrency check automatically.

Second, we define a new interface IRepository where its constraint of T is EntityBase. There, we will write 5 methods: List, Create, Read, Update and Delete. This can call LCRUD.

public interface IRepository<T> where T: EntityBase
{
   IQueryable<T> List();
   void Create(T entity);
   T Read(object keys);
   void Update(T entity);
   void Delete(T entity);
}
Enter fullscreen mode Exit fullscreen mode

Third, we implement a new class GenericRepository using DbContext in Entity Framework.

public abstract class GenericRepository<T> : IRepository<T>
    where T: EntityBase
{
   private readonly DbContext _dbContext;

   public GenericRepository(DbContext dbContext)
   {
      this._dbContext = dbContext;
   }

   public overridable IQueryable<T> List()
   {
      return _dbContext.Set<T>();
   }

   public overridable void Create(T entity)
   {
      _dbContext.Set<T>().Add(entity);
   }

   public overridable T Read(object keys)
   {
      return _dbContext.Set<T>().Find(keys);
   }

   public overridable void Update(T entity)
   {
      _dbContext.Entry(entity).State = EntityState.Modified;
   }

   public overridable void Delete(T entity)
   {
      _dbContext.Set<T>().Remove(entity);
   }
}
Enter fullscreen mode Exit fullscreen mode

Lastly, we create a new class UnitOfWork.

public sealed class UnitOfWork : IDisposable
{
   private readonly DbContext _dbContext;

   public UnitOfWork(DbContext dbContext)
   {
      this._dbContext = dbContext;
      // TODO: Initialize repositories here...
      // e.g. this.StudentRepository
                  // = new StudentRepository(dbContext);
   }

   public bool Save()
   {
      bool isSuccess = _dbContext.SaveChanges() > 0;
      return isSuccess;
   }

   public void Dispose()
   {
      if (_dbContext == null) return;
      _dbContext.Dispose();
   }

   // e.g. public readonly IStudentRepository StudentRepository;
}
Enter fullscreen mode Exit fullscreen mode

Usage

  • Each entity must inherit EntityBase.
  • For each entity, you must create a new interface I[xxx]Repository and a new class [xxx]Repository inheriting GenericRepository and implementing I[xxx]Repository.
  • New Repository classes have to be initialized in UnitOfWork class.

Best Practices:

We can take advantage of IoC frameworks to register DbContext and UnitOfWork into services container and then use UnitOfWork as constructor parameter.

Samples

Suppose StudentDbContext is provided and Ninject is used for registering two services UnitOfWork and StudentDbContext. UnitOfWork will be automatically injected in Studentcontroller.

public class StudentController : ControllerBase
{
   private readonly UnitOfWork _unitOfWork;

   public StudentController(UnitOfWork unitOfWork)
   {
      this._unitOfWork = unitOfWork;
   }
}
Enter fullscreen mode Exit fullscreen mode

A Console application is to add two new Students to database.

public class Program
{
   static void Main(params string [] args)
   {
      DbContext studentDbContext = new StudentDbContext();
      using(UnitOfWork unitOfWork = new UnitOfWork(studentDbContext))
      {
         unitOfWork.StudentRepository
             .Create(new Student("Larry", "Page"))
         unitOfWork.StudentRepository
             .Create(new Student("Mark", "Zuckerberg"))
         unitOfWork.Save();
      }

   }
}
Enter fullscreen mode Exit fullscreen mode

Hopefully, this post can help you to implement your own repository and unit of work.

Happy coding :)

Discussion (4)

Collapse
dyagzy profile image
dyagzy

This is a short and direct to the point explanation of Unit of Work and Repository pattern, your article was very helpful. Thank you for sharing.

Collapse
1001binary profile image
Collapse
filatovv profile image
Yuri Filatov

Great work :)

Collapse
1001binary profile image
Forem Open with the Forem app