Implementation guide to Repository and Unit of Work Patterns in .Net Core

DotNet Full Stack Dev
3 min readDec 28, 2023

--

As you embark on your journey into the realm of .NET Core development, understanding essential architectural patterns like the Repository and Unit of Work becomes paramount.

In this blog post, we’ll demystify these patterns, providing an insightful guide with code snippets to help freshers comprehend their significance in building robust and maintainable applications.

Embark on a journey of continuous learning and exploration with DotNet-FullStack-Dev. https://dotnet-fullstack-dev.blogspot.com/

Background: Building Blocks of .NET Core

.NET Core, a cross-platform, open-source framework, empowers developers to build scalable and high-performance applications. As you delve into the intricacies of .NET Core, familiarizing yourself with architectural patterns becomes crucial for crafting clean, modular, and testable code.

1. The Repository Pattern: Managing Data Access Logic

The Repository Pattern acts as an abstraction layer between the application and the data store. It encapsulates the logic required to retrieve and persist data, providing a clean separation between business logic and data access.

Why Use the Repository Pattern?

  1. Abstraction: Shield the application from the details of data access, enabling easy substitution of data sources.
  2. Testability: Facilitate unit testing by allowing the use of mock repositories.
  3. Maintainability: Centralize data access logic, making it easier to update and maintain.

Implementing the Repository Pattern in .NET Core

public interface IRepository<TEntity>
{
TEntity GetById(int id);
IEnumerable<TEntity> GetAll();
void Add(TEntity entity);
void Update(TEntity entity);
void Delete(int id);
}

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
private readonly DbContext _context;
private readonly DbSet<TEntity> _set;

public Repository(DbContext context)
{
_context = context;
_set = context.Set<TEntity>();
}

public TEntity GetById(int id)
{
return _set.Find(id);
}

public IEnumerable<TEntity> GetAll()
{
return _set.ToList();
}

public void Add(TEntity entity)
{
_set.Add(entity);
}

public void Update(TEntity entity)
{
_set.Update(entity);
}

public void Delete(int id)
{
var entity = _set.Find(id);
_set.Remove(entity);
}
}

In this example, IRepository defines common data access methods, and Repository provides the implementation.

2. The Unit of Work Pattern: Coordinating Repositories

The Unit of Work Pattern ensures that multiple repository operations are treated as a single transaction. It provides a way to coordinate the work of multiple repositories, ensuring data consistency.

Why Use the Unit of Work Pattern?

  1. Transaction Management: Simplify transaction handling by treating multiple repository operations as a single unit.
  2. Consistency: Ensure that changes to multiple repositories are either committed or rolled back together.

Implementing the Unit of Work Pattern in .NET Core

public interface IUnitOfWork : IDisposable
{
IRepository<TEntity> GetRepository<TEntity>() where TEntity : class;
void SaveChanges();
}

public class UnitOfWork : IUnitOfWork
{
private readonly DbContext _context;
private Dictionary<Type, object> _repositories;

public UnitOfWork(DbContext context)
{
_context = context;
_repositories = new Dictionary<Type, object>();
}

public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
{
if (_repositories.ContainsKey(typeof(TEntity)))
{
return (IRepository<TEntity>)_repositories[typeof(TEntity)];
}

var repository = new Repository<TEntity>(_context);
_repositories.Add(typeof(TEntity), repository);
return repository;
}

public void SaveChanges()
{
_context.SaveChanges();
}

public void Dispose()
{
_context.Dispose();
}
}

Here, IUnitOfWork defines a method to get a repository and a method to save changes, and UnitOfWork provides the implementation.

Putting it All Together: Example Usage in .NET Core

public class UserService
{
private readonly IUnitOfWork _unitOfWork;

public UserService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}

public void AddUser(User user)
{
var userRepository = _unitOfWork.GetRepository<User>();
userRepository.Add(user);

// Additional logic, if any...

_unitOfWork.SaveChanges();
}
}

In this example, UserService utilizes the Unit of Work and Repository patterns to add a user to the database.

Conclusion

As a fresher in the world of .NET Core development, embracing architectural patterns like the Repository and Unit of Work is a crucial step towards building scalable and maintainable applications.

These patterns foster clean code, modular design, and effective data access strategies. As you continue your exploration of .NET Core, consider incorporating these patterns into your toolkit to empower your journey as a developer.

Happy coding!

--

--

DotNet Full Stack Dev
DotNet Full Stack Dev

Written by DotNet Full Stack Dev

Join me to master .NET Full Stack Development & boost your skills by 1% daily with insights, examples, and techniques! https://dotnet-fullstack-dev.blogspot.com

Responses (2)