Generic Repository or Specific Repository for Each Entity

Generic Repository or Specific Repository for each entity?

To begin with, if you are using full ORM like Entity Framework or NHibernate, you should avoid implementing additional layer of Repository and Unit Of Work.
This is because; the ORM itself exposes both Generic Repository and Unit Of Work.

In case of EF, your DbContext is Unit Of Work and DbSet is Generic Repository. In case of NHibernate, it is ISession itself.

Building new wrapper of Generic Repository over same existing one is repeat work. Why reinvent the wheel?

But, some argue that using ORM directly in calling code has following issues:

  1. It makes code little more complicated due to lack of separation of concerns.
  2. Data access code is merged in business logic. As a result, redundant complex query logic spread at multiple places; hard to manage.
  3. As many ORM objects are used in-line in calling code, it is very hard to unit test the code.
  4. As ORM only exposes Generic Repository, it causes many issues mentioned below.

Apart from all above, one other issue generally discussed is "What if we decide to change ORM in future". This should not be key point while taking decision because:

  • You rarely change ORM, mostly NEVER – YAGNI.
  • If you change ORM, you have to do huge changes anyway. You may minimize efforts by encapsulating complete data access code (NOT just ORM) inside something. We will discuss that something below.

Considering four issues mentioned above, it may be necessary to create Repositories even though you are using full ORM - This is per case decision though.

Even in that case, Generic Repository must be avoided. It is considered an anti-pattern.

Why generic repository is anti-pattern?

  1. A repository is a part of the domain being modeled, and that domain is not generic.

    • Not every entity can be deleted.
    • Not every entity can be added
    • Not every entity has a repository.
    • Queries vary wildly; the repository API becomes as unique as the entity itself.
    • For GetById(), identifier types may be different.
    • Updating specific fields (DML) not possible.
  2. Generic query mechanism is the responsibility of an ORM.

    • Most of the ORMs expose an implementation that closely resemble with Generic Repository.
    • Repositories should be implementing the SPECIFIC queries for entities by using the generic query mechanism exposed by ORM.
  3. Working with composite keys is not possible.
  4. It leaks DAL logic in Services anyway.

    • Predicate criteria if you accept as parameter needs to be provided from Service layer. If this is ORM specific class, it leaks ORM into Services.

I suggest you read these (1, 2, 3, 4, 5) articles explaining why generic repository is an anti-pattern. This other answer discusses about Repository Pattern in general.

So, I will suggest:

  • Do NOT use repository at all, directly use ORM in your calling code.
  • If you have to use repository, then do not try to implement everything with Generic Repository.

    Instead, optionally create very simple and small Generic Repository as abstract base class. OR you can use Generic Repository exposed by your ORM as base repository if ORM allows it.

    Implement Concrete Repositories as per your need and derive all them from Generic Repository. Expose concrete repositories to calling code.

    This way you get all the good of generic repository still bypassing its drawbacks.

    Even though very rare, this also helps switching ORM in future as ORM code is cleanly abstracted in DAL/Repositories. Please understand that switching ORM is not a primary objective of Data Access Layer or Repository.

In any case, do not expose Generic Repository to calling code.

Also, do not return IQueryable from concrete repositories. This violates basic purpose of existence of Repositories - To abstract data access. With exposing IQueryable outside the repository, many data access decisions leak into calling code and Repository lose the control over it.

do I need to create a repository for each entity or implement a generic repository for the context

As suggested above, creating repository for each entity is better approach. Note that, Repository should ideally return Domain Model instead of Entity. But this is different topic for discussion.

does a generic repository works for EF Database First?

As suggested above, EF itself exposes Generic Repository. Building one more layer on it is useless. Your image is saying the same thing.

Is it good practice to use a generic repository or should each entity have its own repository?

This is something that is heavily debated on the internet. If you use the search here at SO you can find plenty of threads about it. In the end is just comes down to your personal preference. Try both options and decide which one is best for you.

I started creating separate repositories for every entity, but it felt like loads of unnecessary work (and lots of code duplication), so recently I started using generic repositories, and that's working out perfectly for me.

See this thread for more information: Advantage of creating a generic repository vs. specific repository for each object?

Advantage of creating a generic repository vs. specific repository for each object?

This is an issue as old as the Repository pattern itself. The recent introduction of LINQ's IQueryable, a uniform representation of a query, has caused a lot of discussion about this very topic.

I prefer specific repositories myself, after having worked very hard to build a generic repository framework. No matter what clever mechanism I tried, I always ended up at the same problem: a repository is a part of the domain being modeled, and that domain is not generic. Not every entity can be deleted, not every entity can be added, not every entity has a repository. Queries vary wildly; the repository API becomes as unique as the entity itself.

A pattern I often use is to have specific repository interfaces, but a base class for the implementations. For example, using LINQ to SQL, you could do:

public abstract class Repository<TEntity>
{
private DataContext _dataContext;

protected Repository(DataContext dataContext)
{
_dataContext = dataContext;
}

protected IQueryable<TEntity> Query
{
get { return _dataContext.GetTable<TEntity>(); }
}

protected void InsertOnCommit(TEntity entity)
{
_dataContext.GetTable<TEntity>().InsertOnCommit(entity);
}

protected void DeleteOnCommit(TEntity entity)
{
_dataContext.GetTable<TEntity>().DeleteOnCommit(entity);
}
}

Replace DataContext with your unit-of-work of choice. An example implementation might be:

public interface IUserRepository
{
User GetById(int id);

IQueryable<User> GetLockedOutUsers();

void Insert(User user);
}

public class UserRepository : Repository<User>, IUserRepository
{
public UserRepository(DataContext dataContext) : base(dataContext)
{}

public User GetById(int id)
{
return Query.Where(user => user.Id == id).SingleOrDefault();
}

public IQueryable<User> GetLockedOutUsers()
{
return Query.Where(user => user.IsLockedOut);
}

public void Insert(User user)
{
InsertOnCommit(user);
}
}

Notice the public API of the repository does not allow users to be deleted. Also, exposing IQueryable is a whole other can of worms - there are as many opinions as belly buttons on that topic.

How should I manage Generic Repository Pattern when the works of different entities are pretty much different?

How should I manage Generic Repository Pattern when the works of different entities are pretty much different?

This is the core problem with Generic Repository pattern; that is why it is considered an anti-pattern.

I read this here:

No matter what clever mechanism I tried, I always ended up at the same problem: a repository is a part of the domain being modeled, and that domain is not generic. Not every entity can be deleted, not every entity can be added, not every entity has a repository. Queries vary wildly; the repository API becomes as unique as the entity itself.

Why generic repository is anti-pattern?

  1. A repository is a part of the domain being modeled, and that domain is not generic.

    • Not every entity can be deleted.
    • Not every entity can be added
    • Not every entity has a repository.
    • Queries vary wildly; the repository API becomes as unique as the entity itself.
    • For GetById(), identifier types may be different.
    • Updating specific fields (DML) not possible.
  2. Generic query mechanism is the responsibility of an ORM.

    • Most of the ORMs expose an implementation that closely resemble with Generic Repository.
    • Repositories should be implementing the SPECIFIC queries for entities by using the generic query mechanism exposed by ORM.
  3. Working with composite keys is not possible.
  4. It leaks DAL logic in Services anyway.

    • Predicate criteria if you accept as parameter needs to be provided from Service layer. If this is ORM specific class, it leaks ORM into Services.

I suggest you read these (1, 2, 3, 4, 5) articles explaining why generic repository is an anit-pattern.

Better approach is:

  1. Skip the Generic Repository. Implement concrete repositories.
  2. Use Generic Repository as abstract base repository. Derive all concrete repositories from it.

In any case, do not expose generic repository to calling code. Also, do not expose IQueryable from concrete repositories.

Should there be entity-specific methods in the repositories?

Say we have a generic repository with basic operations like Add(), Remove(), GetById()

This is OK if those members are not exposed outside the Data Access Layer. Basically, avoid generic repository. But, as this is not your core of the question, I will not go into the details of it here.

and we also have a specific repository per entity (e.g. ProductRepository, UserRepository, etc.)

This is good practice.

Should we define entity-related operations in the specific repositories or not? For example, in the ProductRepository, should I declare methods like GetProductsInCategory(), GetProductsByBrandId(), etc.

Those functions must be part of specific repository. In fact, that is the purpose of existence of specific repository.

or is this the responsibility of the service layer?

This is opinion based.

Yes; you drop the complete repository layer and do all that stuff in services.

No; abstract the database logic in repositories and keep your services database agnostic. Let them focus on business logic.

So the choice is yours.

The answer you linked suggests alternative to avoid creating too many filter methods in repository.

Instead it is better to have a query method on the repository which takes a Specification. You can pass different implementations of the Specification to retrieve the products.

Although I personally do not agree with it (it leaks data access components in calling layer as you have to accept Specification instance from outside), it may be helpful where you have too much varying filter criteria.

Other point about that answer is that, apparently (that question is tagged as such), it is talking in terms of DDD. You have not mentioned DDD in your question.

In general, I do not agree with most of the claims that answer do; personally.

I'm really confused about the actual methods and operations that we have to implement in a repository.

Specific repositories should implement methods and operations that your calling layer need. If that layer need methods like GetProductsInCategory(), GetProductsByBrandId(), etc. you should implement those. As said above, this is the reason of existence of specific repositories.



Related Topics



Leave a reply



Submit