Disclaimer: In this article I provide demo code implementing the Generic Repository Pattern via .Net6 and C#. This is a high overview and not a step by step guide on how to start the project from scratch. I don't expect everyone to agree with the statements below since there are many different ways to implement this Pattern. Finally, English is not my first language, so my apologies for any mistakes.
The Generic Repository Pattern is a wide used way to keep a neat codebase on the Data Access Layer, keeping commonly used CRUD functionality grouped together, giving the ability to make it reusable among the different entity types.
The core concept of the Generic Repository Pattern is that regardless the passed-in data type, the CRUD functionality will adapt to it and will operate the same way. To achieve this, we make use of C# Generics feature.
A Base Generic Repository (async) interface would like this:
public interface IGenericRepository<T>
{
Task<IEnumerable<T>> AllAsync();
Task<T> GetByIdAsync(string id);
Task<T> CreateAsync(T entity);
Task<T> UpdateAsync(T entity);
Task<string> DeleteAsync(string id);
}
"T" represents the generic type, it could be understood as a "placeholder" for the different data types.
The implementation of the above interface, using Entity Framework, would like this:
public class GenericRepository<T>: IGenericRepository<T> where T: BaseEntity
{
protected readonly DatabaseContext _context;
public GenericRepository(DatabaseContext context)
{
_context = context;
}
public async Task<IEnumerable<T>> AllAsync()
=> await _context.Set<T>().ToListAsync();
public async Task<T> CreateAsync(T entity)
{
entity.Id = Guid.NewGuid().ToString();
entity.DateUpdated = DateTime.Now;
await _context.AddAsync(entity);
await _context.SaveChangesAsync();
return entity;
}
public async Task<string> DeleteAsync(string id)
{
var entity = await _context.Set<T>().FindAsync(id);
_context.Set<T>().Remove(entity);
await _context.SaveChangesAsync();
return id;
}
public async Task<T> GetByIdAsync(string id)
=> await _context.Set<T>().FirstOrDefaultAsync(x => x.Id == id);
public async Task<T> UpdateAsync(T entity)
{
entity.DateUpdated = DateTime.Now;
_context.Set<T>().Update(entity);
await _context.SaveChangesAsync();
return entity;
}
}
As mentioned above, the aim of the base repository is to be shared between other repositories, in order to make use of the already implemented functionality. An example of usage in an individual feature repository interface:
public interface IVehicleRepository : IGenericRepository<Vehicle>
{
}
The above IVehicleRepository
inherits the functionality of the IGenericRepository
, passing the Vehicle
entity to it. In other words, IVehicleRepository
is informs the IGenericRepository
that it is going to use the CRUD methods against the Vehicle
object. And down to the exact feature repository implementation now:
public class VehicleRepository : GenericRepository<Vehicle>, IVehicleRepository
{
protected readonly DatabaseContext _context;
public VehicleRepository(DatabaseContext context) : base(context)
{
_context = context;
}
}
The full implementation of the above code can be found here In this demo project I make use of some of the commonly used techniques, features and tools, including: Generics, DI, Async, Entity Framework (InMemory Database), Automapper, XUnit, FluentAssertation, Moq
If you would like to support this effort to provide tutorials and demo code, please like us on Facebook or even ☕ Buy Me A Coffee
Top comments (0)