I would strictly differ between EF and NH in this case and I would not include both technologies in the same question. Simple NH is more mature and has architecture leading to code which can be more easily tested. Also in case of NH you can simply switch the database to another one (like SQLite) and it will work still the same which doesn't have to be true in case of EF where switching database can result in testing completely different application - especially if you switch between MS and non-MS database.
What is the repository? Let see Martin Fowler's definition:
A Repository mediates between the domain and data mapping layers,
acting like an in-memory domain object collection. Client objects
construct query specifications declaratively and submit them to
Repository for satisfaction. Objects can be added to and removed from
the Repository, as they can from a simple collection of objects, and
the mapping code encapsulated by the Repository will carry out the
appropriate operations behind the scenes. Conceptually, a Repository
encapsulates the set of objects persisted in a data store and the
operations performed over them, providing a more object-oriented view
of the persistence layer. Repository also supports the objective of
achieving a clean separation and one-way dependency between the domain
and data mapping layers.
Nice definition. Now think about the purpose of DbSet
:
- Does it act as in memory collection? Yes you can use it to get entities from database or use
Local
property to get already loaded entities.
- Can client query specifications declaratively? Yes it is called linq-to-entities.
- Can objects be added or removed as from collection? Yes it can.
- Is the mapping encapsulated? Yes it is.
- Is there clean separation? In terms of logic yes. In terms of API no because exposing
IDbSet
to domain model will make the domain model dependent on technology - EF. Is it problem? In theory yes, for purist yes but in 99% it is really not a problem because situation where you need to change the API is rare and it always involves big changes even if you correctly separated APIs.
DbSet
is a repository. The only difference between using DbSet
directly and wrapping it into some generic repository is a separation. This leads to my former answer to similar question - Generic Repository With EF 4.1 what is the point
Now what is a purpose of the repository in your application? I saw your previous questions including this one where you have your BaseRepository
built on top of Entity framework. If you seriously mean this as a base repository which will be parent for your specialized repositories working on aggregate roots for your domain model and exposing specialized methods related only to specific exposed entity type then yes - you are using repository pattern and you need it. But if you are just wrapping context and single set and call this repository you most probably created only redundant layer with doubtful added value because that is just wrapper on top of DbSet
.
There is only single scenario where your repository (DbSet
wrapper) will make sense in such case:
- The wrapper will never expose
IQueryable
(linq-to-entities)
- The wrapper will never accept
Expression<>
and pass it internally to IQueryalbe
(linq-to-entities)
This is the only scenario which will offer you fully mockable repositories => your upper layer can be easily unit tested. You are not going to unit test repositories and you are not going to mock context used in repositories. Repositories wrap data access and mapping logic - the only reasonable tests in case of repositories are integration tests. What is problem of this scenario? You will lose whole power of LINQ and you will have to wrap / re-implement some methods and types implemented in EF. This kind of repositories is the same as used when wrapping data access using stored procedures.
If you don't follow that scenario your live will be much easier. You will have queries defined by LINQ but you will not be able to unit test the code because there is no mock / fake which will still evaluate queries as linq-to-entities. Once you mock DbSet
or IQueryable
you will use linq-to-object which is superset of linq-to-entities. You can easily write a query which will pass a test on mocked DbSet
but fail at runtime with a real DbSet
. Here is more about this problem and here is the example of query which will pass the test but fail at runtime. In this case you must use integration tests (with real database) for all methods using linq-to-entities queries on top of your repositories.