C# — UnitOfWork And Repository Pattern

For data store insulation and test driven development

Kristoffer Karlsson
3 min readFeb 7, 2017

Why the UnitOfWork and Repository pattern?

The UnitOfWork and repository patterns are intended to act like a abstraction layer between business logic and data access layer.
This can help insulate your application from changes in the data store and can facilitate automated unit testing / test driven development.
Source can be downloaded here.

Make sure to have the latest version of EntityFramework included in you project.

pc >  Install-Package EntityFramework

Some Entities

[Table("Authors")]
public class Author {
[Key]
public int ID { get; set; }
public string Name { get; set; } public virtual ICollection<Book> Books { get; set; }
}
[Table("Books")]
public class Book {
[Key]
public int ID { get; set; }
public string Title { get; set; } public int Author_ID { get; set; }

[ForeignKey("Author_ID")]
public virtual Author Author { get; set; }
}

DbContext

public class MyDbContext : DbContext
{
public virtual DbSet<Author> Authors { get; set; }
public virtual DbSet<Book> Books { get; set; }
public MyDbContext(string nameOrConnectionString)
: base(nameOrConnectionString)
{
}
}

The Generic Repository

public interface IRepository<T> where T : class
{
IQueryable<T> Entities { get; }
void Remove(T entity); void Add(T entity);
}
public class GenericRepository<T> : IRepository<T> where T : class
{
private readonly MyDbContext _dbContext;
private IDbSet<T> _dbSet => _dbContext.Set<T>();
public IQueryable<T> Entities => _dbSet;
public GenericRepository(MyDbContext dbContext)
{
_dbContext = dbContext;
}
public void Remove(T entity)
{
_dbSet.Remove(entity);
}
public void Add(T entity)
{
_dbSet.Add(entity);
}
}

The UnitOfWork

public interface IUnitOfWork
{
IRepository<Author> AuthorRepository { get; } IRepository<Book> BookRepository { get; }
/// <summary>
/// Commits all changes
/// </summary>
void Commit();
/// <summary>
/// Discards all changes that has not been commited
/// </summary>
void RejectChanges();
void Dispose();
}
public class UnitOfWork : IUnitOfWork
{
private readonly MyDbContext _dbContext;
#region Repositories public IRepository<Author> AuthorRepository =>
new GenericRepository<Author>(_dbContext);
public IRepository<Book> BookRepository =>
new GenericRepository<Book>(_dbContext);
#endregion public UnitOfWork(MyDbContext dbContext)
{
_dbContext = dbContext;
}
public void Commit()
{
_dbContext.SaveChanges();
}
public void Dispose()
{
_dbContext.Dispose();
}
public void RejectChanges()
{
foreach (var entry in _dbContext.ChangeTracker.Entries()
.Where(e => e.State != EntityState.Unchanged))
{
switch (entry.State)
{
case EntityState.Added:
entry.State = EntityState.Detached;
break;
case EntityState.Modified:
case EntityState.Deleted:
entry.Reload();
break;
}
}
}
}

Note how it goes from Entity => Repository => UnitOfWork

We have two entities, Book and Author. Each entity has it own repository inside the UnitOfWork. The UnitOfWork takes our DbContext as parameter and handles everything that has to do with it, Commit, Reject changes and managing our repositories, AuthorRepository and BookRepository.

Usage

var dbContext = new MyDbContext("{connectionString goes here}");
var unitOfWork = new UnitOfWork(dbContext);

Selecting some entities

var books = unitOfWork.BookRepository.Entities
.Where(n => n.Title == "Hello World");
var justOneBook = unitOfWork.BookRepository.Entities
.First(n => n.ID == 1);

Create

// Create
var author = new Author() { Name = "Kris" };
unitOfWork.AuthorRepository.Add(author);unitOfWork.Commit(); // Save changes to database

Update

// Update
var author = unitOfWork.AuthorRepository.Entities
.First(n => n.Name == "Kris");
author.Name = "Dan";unitOfWork.Commit(); // Save changes to database

Delete

// Delete
var author = unitOfWork.AuthorRepository.Entities
.First(n => n.Name == "Dan");
unitOfWork.AuthorRepository.Remove(author);unitOfWork.Commit(); // Save changes to database

Reject Changes

// Delete
var author = unitOfWork.AuthorRepository.Entities
.First(n => n.Name == "Dan");
unitOfWork.AuthorRepository.Remove(author);// Ops i don't want to do thisunitOfWork.RejectChanges(); // Reject all changes

Testing

You can easily write unit tests by simply mocking the DbContext methods. For example you could mock the Entities property to always return a fake list of objects. That way the DbContext won’t make any external calls to your data layer.

Source

Source can be downloaded here.

Found this useful? Don’t forget to follow me on twitter.

BTC

1CGu9Ctt1AuyXiWMJ2nEDoH1RRAKtStdjx

ETH

0xd2291b554075da7f61210db2648a7f0a2d006190

XRB

xrb_3fw49ebj4yfyzpxjeb474678hdgp1d69udctkgww4yzfhaa8oeye6o3sg98f

--

--

Kristoffer Karlsson

Software developer and indie game developer from Sweden. Been turning coffee into code since 2010.