Repository or DAO?: Repository
Fine, fine, the title is a blatant copy from Fabio Maulo’s equally titled blog post.
Then again, this was the direct inspiration for understanding how the data access should look like in the mini-Blog engine I am cooking up. I decided to implement a repository just like Fabio recommends (but doesn’t follow himself if you watch the comments) and see how far it gets me while already pointing out some shortcomings and how to overcome them.
The basic idea is to have a repository that looks like this:
Fabio already showed us how to implement the queryable - by exposing the LINQ to NHibernate capabilities through your repository. LINQ to NH is available in a version 1.0 provider. It may not support all things that could be thought about when writing LINQ queries but so far it has not disappointed…here’s a simple query from one of my tests…
Any code that uses a repository is ridiculously easy to unit test. Fleshing out a test repository means e.g. writing the following class:
which leaves you implementing the queryable interface. For this we have the extension method AsQueryable, e.g.:
What Fabio did not show was the implementation of the ICollection interface. Here’s the signature of ICollection as a reminder:
Hence, the ICollection gives you semantics to add and remove items from it and know the count. What it doesn’t give you is List semantics which adds capabilities based on an index.
Implementation of ReadOnly is pretty clear: return false. Implementation of CopyTo and Clear is also pretty straight forward in my implementation:
Add maps pretty well to a Save operation, thus the implementation uses the Session’s save. However, the Session’s save has the nice notion of returning the Primary key that was given to the newly saved entity while Add’s return signature is void.
Getting to know the Id is quite practical, if only to reference the same entity again in Unit Tests involving DB interaction. Therefore, the IRepository<T> interface gets the following addition:
Note that the generic type argument of Repository is restricted to my base class for persisted objects, Entity, which comes along with an Id-property of type int. Hence, it should be safe to limit myself to int as the primary key.
As a result, the implementation looks as follows:
Making the ICollection implementation explicit makes the two _Add_s distinguishable for the compiler. Usually the void Add will be hidden and Add will return you with the id assigned to your object. If something depends purely on the ICollection interface, only the collection semantics will be available
Implementing Contains is somewhat trickier - possibly a reason why NHibernate’s LINQ Provider does not support LINQ’s Contains method. Since you are looking p a dehydrated object on the DB, what does Contains mean when you enter the collection with a fresh instance?
One possible answer would be to check whether the object passed in is associated with the current session…
I have opted for a simple Id-comparison - here Contains states whether an item is contained that has the same primary key as the one of the item passed in (remember that the type argument of the Repository is restricted to be an Entity):
Remove maps quite logically to a Delete:
This time NH’s return value is void, while the Collection supposedly wants you to state whether a Remove did happen or not. In this simple implementation I do not want to bother.
Finally we may return a Count which maps quite nicely to SQL’s count:
As a final touch, simple retrieval by Id is not as simple as it should be. I include the following into the IRepository interface:
With an equally brief implementation:
There is the first version of a Repository that mostly acts like an in-memory collection of objects. It can be queried with LINQ and objects can be added or removed.
Hang on…Mostly?
Yes. It is still up to powers beyond the repository to ensure that e.g. in mutating operations the NHibernate Session that is used gets its stuff written to the DB. That is, it is the responsibility of repository users to ensure transactional security, flushing, etc.
Remember those nasty Stored Procs written by demented people that would commit after doing some stuff, not caring about the bigger picture? Same applies here.
For now it seems that the repository will cover a number of common DB needs for the targetted project - thanks goes to Fabio for the nice inspiration!