Linq Expressions, The Specification Pattern and Repositories - Part 3

In the previous to parts to this series (here and here) I showed the basic infrastructure framework for using Linq-based specifications with a flexible repository interface. In this article I dig deeper into the Expression based find functionality and it's implementation.

The Goal

Specification based query support on the repository is a great feature for encapsulation of any query logic. My major problem with this approach was the creation of trivial specifications on a per-domain-object basis. This would litter the code with specifications that had very little functionality contained within them. Generally this is not a bad thing but, in this instance, for the sake of maintainabilty and simplicity I wanted a to be able to perform ad-hoc queries.

The solution was to make my repository able to accept queries like :

            var repository = new Repository<Person>(session);
            
var people repository
                .FindAll(p 
=> p.Name == "steve");

The AdHoc specification

Since I have already made the decision that my MatchingCriteria are of type Expression, the AdHoc specification is trivial - but very cool. The implementation looks like :

    public class AdHoc<T> : Specification<T> where T : IEntity
    {
        
private readonly Expression<Func<T, bool>> expression;

        public 
AdHoc(Expression<Func<T, bool>> expression)
        {
            
this.expression expression;
        
}

        
public override Expression<Func<T, bool>> MatchingCriteria
        {
            
get return expression}
        }
    }

Simplifying the repository using the AdHoc specification

By utilising this simple but powerful specification class we can simplify the api to our IRepository. We are now able to add a FindOne and FindAll overload that accept Linq based queries in an AdHoc fashion. The implementation is as simple as :

        public IQueryable<T> FindAll(Expression<Func<T, bool>> expression)
        {
            
return FindAll(new AdHoc<T>(expression));
        
}

This functionality is able to be consumed in the following fashion (satisfying our goal for this task) :

            var people repository.FindAll(p => p.Name == "steve");

Conclusion

In combination with the flexibility built into the IRepository interface we now have a basis for a data access layer with maximum flexibility. Sql queries are created at execution time to reflect the criteria that we have applied through domain specifications - be they concrete or ad-hoc.

kick it on DotNetKicks.com

August 12, 2008 12:44 by steven.burman
E-mail | Permalink | Comments (2) | Comment RSSRSS comment feed

Related posts

Comments

October 24. 2008 04:03

Jean-Philippe Leconte

Your implementation looks mighty queer to me. You have an IRepository interface that exposes the IQueryable interface. Why not make the IRepository interface derive from IQueryable; the implementation would simply wrap the top IQueryable for a type (DataContext.GetTable<T> in LinqToSQL, ISession.Linq<T> in NHibernate iirc), making it completely available to Linq. Instead of having to reimplement FindOne, FindAll, etc... you could simply use First, FirstOrDefault and Where...

Jean-Philippe Leconte

October 24. 2008 10:06

steven.burman

@Jean-Philippe

Interesting approach but doesn't fit in with my need to define specifications and have them query against the database.

In fact, more recently I am actually returning IEnumerable from the repository so I can be assured that I have no late sql execution when I am not expecting it.

steven.burman

Add comment


(Will show your Gravatar icon)  

  Country flag

[b][/b] - [i][/i] - [u][/u]- [quote][/quote]



Live preview

January 6. 2009 13:55