I’ve read a lot of blog posts about the Repository pattern. Whenever I have looked at implementing it, I always come away underwhelmed. It’s one of those things that displays nicely in a small example but in practice you end up with a huge number of methods that each query the data in a similar but slightly different manner, and for me it’s fault is with querying.
GetCustomersByFirstName();
GetCustomersByFirstAndLastName(); GetCustomersByFirstAndLastAndThreeOfTheirFourChildrensNames();
Now granted I made that last one up, but I have seen Repository examples that are nearly as silly.
The story is even worse when you are trying to bring back data in a form that does not exactly match your data structure. The pattern feels very restrictive to me with each repository matching an entity in the database.
LINQ is a tempting way of getting around the querying problems by exposing a LINQ provider for your datasource to the other areas of the application. Which in certain cases I am all for, but in a large enough application this really isn’t that different from just letting the business layer execute it’s own SQL. Sure you might get some strong typing, but a basic structure change in the database can still see you hunting all over for references that need fixing.
Then one day I saw in passing someone comment they preferred to use Query objects now over Repositories. A quick internet search didn’t turn up anything I considered a pattern so I set about thinking what might be meant by a Query object, and quickly convinced myself this is what I was really after.
The basic requirements I had were a data layer that worked with POCO objects, able to be created as an interface to allow a secondary implementation and a focus on usable querying for both straight entities from the database and ad-hoc queries as needed.
The objects I ended up with were fluent wrappers for basically a set of query options. These options are then used to construct a query using whatever method of database connection the app is using. Here is an example of a query to give you an idea of how it works.
IList<Customer> = db.CustomerQuery()
.FirstName_Contains("Fred")
.LastName_Is("Blog")
.Age_Between(18, 35)
.FirstName_Sort(SortOrder.Ascending)
.List();
And here is an example of how to implement FirstName_Contains() in an implementation of the query layer that uses NHibernate.Linq as it’s data provider.
public ICustomerQuery FirstName_Contains(string name) {
if (!String.IsNullOrEmpty(name))
Query = Query.Where(d => d.FirstName.Contains(name));
return this;
}
Obviously that implementation code depends heavily on how you talk to your database. Before I started to utilise NHibernate.Linq it looked more like this
Criteria.Add(Expression.Like("FirstName", name)
So I now use a single database object that can load and save an entity from any table in the database, and it can also set up a query object for me to fill out with the appropriate options for that query and execute it.
I find this a nice middle ground between needing every query to have it’s own method and the open slate that comes from exposing a LINQ provider or SQL statements.
I won’t pull apart one of the projects that uses this to offer a full example of every method, but it is simple to add features like Top(), Take()/Skip(), even Count().
1 comment:
I must say this looks much like my EntityFilter<T> concept. Here's a code snippet:
// Create a new entity filter in the presentation layer
// Here I'm using LINQ queries, but using extension methods is possible too.
IEntityFilter<Person> personFilter =
from person in EntityFilter<Person>.AsQueryable()
where person.Name.StartsWith("a")
where person.Id < 100
select person;
// Call the service layer
var persons = PersonRepository.GetAll(personFilter);
// Here is the service layer code
public static Person[] GetAll(IEntityFilter<Person> personFilter)
{
using (var db = ContextFactory.CreateContext())
{
IOrderedQueryable<Person> filteredList =
personFilter.Filter(db.Persons);
return filteredList.ToArray();
}
}
You can find the code on CodePlex here and I wrote about it here (but please note that I mainly talk about a similar concept called EntitySorter<T>).
I hope this code is of some use to you.
Cheers.
Post a Comment