5

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).

2 Answers 2

3

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)

Sign up to request clarification or add additional context in comments.

12 Comments

That's certainly a solution, but that's a mixing of responsibilities (encapsulating querying and fetching strategies). This is why the repository pattern is weak.
True, but you can always separate the fetching strategies into another class, or make FetchRelated a property that can be set by a service layer.
@Diego Mijelshon: What are we gaining from that extra level of abstraction?
@Jason what are we losing with this approach? :-) my point is, you can separate it however you want; the advantage of putting it in the Specification is that no interfaces change.
@Diego Mijelshon: More code to design, develop, test and maintain. That has a cost including but not limited to opportunity cost.
|
1

You wouldn't want to put the Fetch() call into the specification, because it's not needed. Specification is just for limiting the data that can then be shared across many different parts of your code, but those other parts could have drastically different needs in what data they want to present to the user, which is why at those points you would add your Fetch statements.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.