Feel like a geek and get yourself Ema Personal Wiki for Android and Windows

28 August 2009

Wrapping the NHibernate fetchmode

Configuring the NHibernate fetchmode and lazy/eager loading has two purposes: query efficiency (reduce database hits wherever possible) and preventing the dreaded LazyInitializationException. Most properties are configured for lazy loading, because this is a sensible default. This means you often have to tweak the fetchmode for particular queries in a particular way.

But combining queries and different fetchmodes scenario's will lead to a cartesian product of possibilities if used in a traditional, repository-like way.

For example:
  • IList<Author> GetAuthors()
  • IList<Author> GetAuthorsWithArticles()
  • IList<Author> GetAuthorsWithBooks()
  • IList<Author> GetAuthorsWithBooksAndArticles()
  • IList<Author> GetAuthorsByLastname(string lastname)
  • IList<Author> GetAuthorsByLastnameWithBooks(string lastname)
  • etc...

This looks like a bad design.

The solution is the Query Object pattern (here and here). This pattern wraps queries in objects.

If the query is an object, and the fetchmode can also be configured in an object-oriented way, applying fetchprofiles to a query and executing the query looks like this:
// .. get session and transaction somewhere ..

var results = 
  new AuthorsByNameQuery()
    .LastName("Adams")
    .WithBooks()
    .WithArticles()
      .List(session);

The query object wraps the criteria and fetchmode:
class AuthorsByNameQuery 
{
  internal DetachedCriteria Criteria;

  public AuthorsByNameQuery()
  {
    Criteria = DetachedCriteria.For<Author>();
  }

  public IList<Author> List(ISession session)
  {
    return Criteria
      .GetExecutableCriteria(session)
      .List<Author>();
  }

  public AuthorsByNameQuery Lastname(string lastname)
  {
    Criteria.Add(Expression.Eq("Lastname", lastname));
    return this;
  }

  public AuthorsByNameQuery WithBooks()
  {
    Criteria.SetFetchMode("Books", FetchMode.Select);
    return this;
  }
  public AuthorsByNameQuery WithArticles()
  {
    Criteria.SetFetchMode("Articles", FetchMode.Select);
    return this;
  }
}

I have been using queries and fetchprofiles this way in a few projects now and it really helps to structure the possibilities.

No comments: