While preparing the code for my previous post, I decided to try NU to get the OSS I needed for the demo. I must say it was a blast. Ruby is required to run NU. For those who don’t have ruby on their machine, get the ruby installer. It won’t slow your system down or pollute it in any way. It’s just creating a directory of your choice for ruby, and adds a system path pointing to it.
I wanted fluent Nhibernate with all it’s compatible dependencies: NHibernate, Castle.core, … To get all these dependencies, all I had to do is this:
- Open the command prompt
- Install NU: gem install nu (you only need to do this once, obviously)
- Navigate to the project folder: cd I:\projects\RepositoryExperiments
- Get FluentNH and all its dependencies: nu install fluentnhibernate
That’s it. My project directory now has a lib folder containing:
- castle.core
- castle.dynamicproxy2
- fluentnhibernate
- log4net
- nhibernate
And it took me about 30 seconds to set this up.
Way to go NU! These are the packages currently available with NU: http://nu.wikispot.org/Current_Packages
Since I published the state of my project goals, I got a few questions about my repository implementation, so here goes…
The ‘classic’ repository interface looks like this:
public interface IRepository<T>
{
T GetById(int id);
IEnumerable<T> GetAll();
T SaveOrUpdate(T entity);
void Delete(T entity);
//...
}
This interface has some issues to me. First of all, it’s data-centric. I know that’s the whole point of a repository, but bear with me. Second, it exposes far too many methods. One method in particular scares me: GetAll()! This is the one method you REALLY don’t want to execute in production when you have massive amounts of data. And if you’re lucky, you will have a few eager fetches with a nice SELECT N+1. Yikes.
The implementations of this interface tend to become a long list of different queries.
public class CustomerRepository : IRepository<Customer>
{
public Customer GetById(int id) {...}
public IEnumerable<Customer> GetAll(){...}
public Customer SaveOrUpdate(Customer entity){...}
public void Delete(Customer entity){...}
public IEnumerable<Customer> GetByName(string customerName){...}
public IEnumerable<Customer> GetByTotalOrderAmount(double orderAmount){...}
public IEnumerable<Customer> GetByCity(string zipCode){...}
public IEnumerable<Customer> GetNewCustomersSince(DateTime date){...}
...
}
An alternative
I’ve been looking for an alternative to this. The goals I had in mind are:
- I want an easy interface to interact with the database
- Every time I need a new query, I don’t want to have to add a new method to an existing object. I want a new object that encapsulates the query.
- I want to be free to choose which query API call I make against NHibernate: LINQ, criteria or hql.
A single IRepository
My IRepository interface is a simple one:
public interface IRepository
{
void Add<T>(T entity);
void Remove<T>(T entity);
IQueryResult<T> Query<T>(IQuery<T> query);
}
Add() and Remove() just look and feel like standard collection methods, and the implied behavior is simply forwarded to the database. Since I’m using NHibernate under the hood, there’s no need to have a Save() or an Update() statement, NHibernate’s autoflush will take care of this.
The interesting part is the last method: Query(). This method takes a query object, and returns a query result object. When you call this method, you can do this:
var customer = repository.Query(new GetCustomerByName("ALFKI"))
.UniqueResult();
Lets take a closer look at the repository implementation:
public class NHibernateRepository : IRepository
{
public void Add<T>(T entity)
{
CurrentSession.Save(entity);
}
public void Remove<T>(T entity)
{
CurrentSession.Delete(entity);
}
public IQueryResult<T> Query<T>(IQuery<T> query)
{
return query.Execute(CurrentSession);
}
private static ISession CurrentSession
{
// use whatever suits you ... out of scope of this post
}
}
IQuery<T>
The query object can use the NHibernate query, criteria or LINQ, the repository doesn’t care. This offers several advantages:
- You only need one repository (or DAO, or whatever you want to name it)
- The repository implementation doesn’t have to change every time you need a new query
- You can test each query object in isolation
- Your services have to reference only one repository
Lets go and take a look at what a query object implementation looks like:
public class GetCustomerByName : IQuery<Customer>
{
private readonly string name;
public GetCustomerByName(string name)
{
this.name = name;
}
public IQueryResult<Customer> Execute(ISession session)
{
var query = session.CreateQuery("from Customer where Name=:name")
.SetString("name", name);
return new QueryResult<Customer>(query);
}
}
Again, for someone familiar with NHibernate, no magical tricks here. This is where you can choose what NHibernate API you want to use. Her goes the criteria API:
public IQueryResult<Customer> Execute(ISession session)
{
var criteria = session.CreateCriteria<Customer>().Add(Restrictions.Eq("Name", name));
return new CriteriaResult<Customer>(criteria);
}
And the LINQ API:
public IQueryResult<Customer> Execute(ISession session)
{
var query = session.Linq<Customer>().Where(c => c.Name == name);
return new LinqResult<Customer>(query);
}
IQueryResult<T>
The IQueryResult<T> interface should speak for itself. Once you have performed a query against the repository, you can either get its unique result, or a list of results.
public interface IQueryResult<T>
{
T UniqueResult();
IEnumerable<T> List();
}
At this time of writing, you only need three implementations of IQueryResult<T>: QueryResult<T>, CriteriaResult<T> and LinqResult<T>. The implementation you choose to use is free and the decision is made inside the query object, exactly where you choose which query type you want to make.
public class QueryResult<T> : IQueryResult<T>
{
private readonly IQuery query;
public QueryResult(IQuery query)
{
this.query = query;
}
public T UniqueResult()
{
return query.UniqueResult<T>();
}
public IEnumerable<T> List()
{
return query.List<T>();
}
}
public class CriteriaResult<T> : IQueryResult<T>
{
private readonly ICriteria criteria;
public CriteriaResult(ICriteria criteria)
{
this.criteria = criteria;
}
public T UniqueResult()
{
return criteria.UniqueResult<T>();
}
public IEnumerable<T> List()
{
return criteria.List<T>();
}
}
public class LinqResult<T> : IQueryResult<T>
{
private readonly IQueryable<T> query;
public LinqResult(IQueryable<T> query)
{
this.query = query;
}
public T UniqueResult()
{
return query.First();
}
public IEnumerable<T> List()
{
return query;
}
}
Drawbacks of this implementation
Maybe I could split the IRepository in two: one interface for data-modifying methods (as in command - CQS), and a second interface for querying (as in Query - CQS). At this moment I don’t see the need to do this, but it’s an idea I keep in the back of my head.
One drawback to this implementation that I’m aware of: the ISession interface of NHibernate leaks out of the declared interfaces. And you know what: I don’t care. Don’t kid yourself, NHibernate has a huge impact on the design and architecture of the application. I’m on Ayende’s side on this one: don’t try to abstract your DAL, it doesn’t work and you’ll find out too late.
Another design issue as it is implemented right now, the services querying the repository have to know the implementation of the query objects. I’m not sure this is a real drawback. Any ideas are welcome.
Code available for download
Since the whole point of this blog post was showing you some code, I posted it in a small google project. You can download it and run it, it works fine with SQLite in memory, no install necessary, no database required. I have a felling some of you running 64-bit might run into some problems with SQLite. Contact me if this happens.