What is the Repository pattern, and do you need it with EF Core?
Quick Answer
The Repository pattern puts a collection-like abstraction (IOrderRepository with Add, GetById, Find) between your domain and the data store, so business code doesn't know about SQL or EF. Whether you need it over EF Core is debated: DbSet<T> already is a repository and DbContext already is a unit of work, so a thin generic repository often just adds a leaky layer.
The Repository pattern is a collection-style abstraction over data access — the domain talks to IOrderRepository, not to SQL or EF. The interesting part is whether it's worth it with EF Core, because DbSet is already a repository and DbContext is already a unit of work. A thin generic Repository<T> usually hurts — either it returns IEnumerable and you lose server-side querying, or it returns IQueryable and leaks EF anyway. If I use a repository I make it specific per aggregate with named methods like GetOrdersAwaitingShipment, so it actually expresses intent. In a small app I'm fine using the context directly.
The case for a repository: testability without a database, a seam to swap data sources, centralising query logic, keeping EF types out of the domain, and enforcing aggregate boundaries in DDD.
The case against (especially a generic Repository<T>):
- EF Core's
DbSet<T>isIQueryable— a generic repo that returnsIEnumerablekills server-side filtering; one that returnsIQueryableleaks EF anyway. - You lose EF features (
Include, split queries,AsNoTracking) or re-expose them through the interface. - EF Core has an in-memory / SQLite provider, so “testability” is weaker than it used to be.
Pragmatic middle ground: use specific repositories per aggregate with intention-revealing methods (GetOrdersAwaitingShipment()), not a generic CRUD repo. Or use the context directly in small apps and accept the coupling.
- A generic
IRepository<T>that returnsIEnumerable<T>— every query now materialises the whole table before filtering. - Wrapping EF just to "follow the pattern" while the interface exposes
IQueryableand EF-specific methods — no real decoupling. - Combining a repository with a separate Unit of Work when
DbContextalready provides both. - Putting business logic in the repository instead of in the domain or a service.