Hi looking at the repository pattern which commonly seems to be implemented something like:
public class GenericRepository<TEntity> where TEntity : class
{
// other business
public virtual TEntity GetByID(object id)
{
return db.Set().Find(id);
}
public virtual void Insert(TEntity entity)
{
db.Set().Add(entity);
}
public virtual void Delete(object id)
{
TEntity entityToDelete = db.Set().Find(id);
Delete(entityToDelete);
}
public virtual void Update(TEntity entityToUpdate)
{
db.Set().Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
}
So for every type you want to work with (ie update) you need to instantiate a repository.
So if I had two types I wanted to save Cars
and Trucks
I would need to go:
var carRepository = new GernericRepository<Car>();
carRepository.Update(myCar);
var truckRepository = new GernericRepository<Truck>();
carRepository.Update(myTruck);
So then you have seperate repositories for each type. To make sure you save everything at once you need the unitOfWork
to ensure they all use the same context and save at one time.
Surely wouldn't it be better to have something like:
public class GenericRepository
{
// other business
public virtual TEntity GetByID<TEntity>(object id) where TEntity : class
{
return db.Set<TEntity>().Find(id);
}
public virtual void Insert<TEntity>(TEntity entity) where TEntity : class
{
db.Set<TEntity>().Add(entity);
}
public virtual void Delete<TEntity>(object id) where TEntity : class
{
TEntity entityToDelete = db.Set<TEntity>().Find(id);
Delete(entityToDelete);
}
public virtual void Update<TEntity>(TEntity entityToUpdate) where TEntity : class
{
db.Set<TEntity>().Attach(entityToUpdate);
context.Entry(entityToUpdate).State = EntityState.Modified;
}
}
This means the repository only needs to be instantiated once and therefore is truely generic?
So you could update your cars and trucks like this:
var repository = new GernericRepository<Car>();
repository.Update<Car>(myCar);
rRepository.Update<Truck>(myTruck);
Surely this is a better method? Am I missing something? It automatically has only one context too.
The repository pattern does not decouple the data access from the data store, that is what the ETL tool such as NHibernate or the Enity Framework does for. The repository pattern provides reusable methods for extracting data.
I have previously used a so called "Generic" repository as you have described and thought it was great. It isn't until you realise that you have just put another layer on top of NHibernate or the Entity Framework you realise it's all gone Pete Tong.
Ideally what you want are interfaces that describe ways of getting data out of your data store and should not leak what data access you are using. For example:
public interface IEmployee
{
IEmployee GetEmployeeById(Guid employeeId);
IEmployee GetEmployeeByEmployeeNumber(string employeeNumber);
IEnumerable<IEmployee> GetAllEmployeesWithSurname(string surname);
IEnumerable<IEmployee> GetAllEmployeesWithStartDateBetween(DateTime beginDateTime, DateTime endDateTime);
}
This gives you a contract to code to, there is no knowledge of your persistence layer and the queries used to retrieve the data can be unit tested in isolation. The interface could inherit from a base interface that provides common CRUD methods but you would be assuming that all your repositories would need CRUD.
If you go down the road of a Generic Repository you will end up with duplication in your queries and you will find it much harder to unit test the code that uses the repository as you will have to test the queries as well.
Generics by itself does not make an implementation of the repository pattern. We've all seen the generic base class used in example repository pattern implementations but this is to make things DRY (Don't-Repeat-Yourself) by inheriting from the base class ( GenericRepository
in your case) to more specialized child classes.
Only using the generic, base class GenericRepository
assumes that your repositories will only ever need the most basic CRUD methods. For a more complex system, each repository becomes more specialized based on underlying business entities data requirements.
Also, you will need to have interfaces that define your data contracts with your other layers. Using the repository pattern means you don't want to expose your concrete implementations of your repositories to your other layers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With