Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using eager loading with specification pattern

I've implemented the specification pattern with Linq as outlined here https://www.packtpub.com/article/nhibernate-3-using-linq-specifications-data-access-layer

I now want to add the ability to eager load and am unsure about the best way to go about it.

The generic repository class in the linked example:

public IEnumerable<T> FindAll(Specification<T> specification)
{
  var query = GetQuery(specification);
  return Transact(() => query.ToList());
}

public T FindOne(Specification<T> specification)
{
  var query = GetQuery(specification);
  return Transact(() => query.SingleOrDefault());
}

private IQueryable<T> GetQuery(
  Specification<T> specification)
{
  return session.Query<T>()
    .Where(specification.IsSatisfiedBy());
}

And the specification implementation:

public class MoviesDirectedBy : Specification<Movie>
{

 private readonly string _director;

 public MoviesDirectedBy(string director)
 {
   _director = director;
 }

 public override
    Expression<Func<Movie, bool>> IsSatisfiedBy()
 {
   return m => m.Director == _director;
 }
}

This is working well, I now want to add the ability to be able to eager load. I understand NHibernate eager loading can be done by using Fetch on the query.

What I am looking for is whether to encapsulate the eager loading logic within the specification or to pass it into the repository, and also the Linq/expression tree syntax required to achieve this (i.e. an example of how it would be done).

like image 680
Simon Avatar asked Oct 14 '22 20:10

Simon


1 Answers

A possible solution would be to extend the Specification class to add:

public virtual IEnumerable<Expression<Func<T, object>>> FetchRelated
{
    get
    {
        return Enumerable.Empty<Expression<Func<T, object>>>();
    }
}

And change GetQuery to something like:

        return specification.FetchRelated.Aggregate(
            session.Query<T>().Where(specification.IsSatisfiedBy()),
            (current, related) => current.Fetch(related));

Now all you have to do is override FetchRelated when needed

public override IEnumerable<Expression<Func<Movie, object>>> FetchRelated
{
    get
    {
        return new Expression<Func<Movie, object>>[]
                     {
                         m => m.RelatedEntity1,
                         m => m.RelatedEntity2
                     };
    }
}

An important limitation of this implementation I just wrote is that you can only fetch entities that are directly related to the root entity.

An improvement would be to support arbitrary levels (using ThenFetch), which would require some changes in the way we work with generics (I used object to allow combining different entity types easily)

like image 168
Diego Mijelshon Avatar answered Nov 15 '22 09:11

Diego Mijelshon