Implement Repository Base and Unit of Work in C#
Tags: dotnet, csharp, designpatterns, architecture
Published: 2021-04-15 14:10:52

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

```csharp
public class EntityBase
{
   public int Id { get; set; }
   public byte[] Version { get; set; }
}
```

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<T> where its constraint of T is EntityBase. There, we will write 5 methods: List, Create, Read, Update and Delete. This can call LCRUD.

```csharp
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);
}
```

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

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

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

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

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

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

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

   public virtual void Delete(T entity)
   {
      _dbContext.Set<T>().Remove(entity);
   }
}
```

Lastly, we create a new class UnitOfWork.

```csharp
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;
}
```

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

```csharp
public class StudentController : ControllerBase
{
   private readonly UnitOfWork _unitOfWork;

   public StudentController(UnitOfWork unitOfWork)
   {
      this._unitOfWork = unitOfWork;
   }
}
```

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

```csharp
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();
      }
      
   }
}
```

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

Happy coding :)