Click here to Skip to main content
15,867,568 members
Articles / Web Development / ASP.NET

Composed LINQ Queries using the Decorator Pattern

Rate me:
Please Sign up or sign in to vote.
4.71/5 (14 votes)
28 Jun 2010CPOL4 min read 49.9K   332   32   8
Design Patterns Series - Part I

Contents

Introduction

This article series will explore practical uses and applications of design patterns in day to day development practices, using practical examples in C#. The first article (one of hopefully many more to come) will cover the Decorator Pattern. This series is also published on my Blog C# | B# | Stay# [^] which as its name implies celebrates the #-ness in all things!

Background and Concepts

Let’s kick off this post with some theory. According to the GoF, the intent of the decorator design pattern is to:

"Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality."

That was quoted from their seminal work Design Patterns: Elements of Reusable Object-Oriented Software [^] In my humble opinion, one of the greatest pleasures working in software development is when we can successfully implement a pattern, not through a premeditated intent but through natural and organic development of the code at hand. We spot the variation and try to encapsulate it and then lo and behold, we end up using one pattern or another fulfilling its intent and purpose.

I think the holy grail of OO software developers (a slightly conceited statement perhaps) is to identify what is common and what varies in any given requirement and then find a way to encapsulate that change making future (and almost inevitable requirement changes) relatively easy to implement.

After that brief techno-rant, we can go back to our main topic. The Decorator Design Pattern. Conceptually, here is how the pattern looks like:

decorator_pattern.jpg

The decorator pattern allows me to create a chain of objects (trail of decorator objects) namely, the decorators that are responsible for the new functionality, and ends with the original object. And the call chain looks like:

CallChain.jpg

This is not to be confused with a linked list. Rather it should be regarded as a collection of optional decorating objects.

A Classic Example

One classic example of this pattern is the stream I/O library. For any particular stream, there is only one input, but there can be zero or more actions to perform on the input stream.

For instance, as in the example below, I can read from a memory stream, filter the stream and then read it using a custom stream reader. All these components (the later two are custom objects) implement the stream abstract class and accept a stream object in their constructors. The chain of calls looks like this:

C#
Stream filteredMemoryStream = new StreamReader(new StreamFilter(new MemoryStream()));

Query Decorators

Personally, I use the decorator pattern to solve the problem of decomposing LINQ queries into more granular and reusable parts. A client can call several LINQ queries that are similar but only have minor variations. The direct result of this is LINQ code duplicated in every query. Another side effect is the tight coupling between the calling client and query composition.

The fictitious example I use here assumes we're querying an Employees repository and there are multitude of queries that might contain the same expressions and clauses. Say, we need to get all employees who are managers. Another requirement is that we get all employees who are managers and of certain age. We may then need to get the top %20 of those managers. All these extra queries are transformed into reusable decorators as follows:

QueryDecorator.png

As a direct result of this design, the query composition can be chained inside a factory that instantiates the desired query based on simple conditional logic of perhaps some configuration file:

C#
public class EmployeeQueryFactory
    {
        public static QueryComponent<Employee> GetQuery()
        {            
            /*             
             * We decouple the calling client from the query component instantiation
             * by using this factory method where we encapsulate all conditional       
             * logic that determines which query component to instantiate 
             */

            //Get all employees who are contractees            
            return new ContracteesQuery(new EmployeeQuery());            
            
            //Or get all managers older than 50             
            return new AgeLimitQuery(new ManagersQuery(new EmployeeQuery()), 50);       
            
            //Or get top %20 of all contractees older than 30
            return new Top20PercentQuery(new AgeLimitQuery
		(new ContracteesQuery(new EmployeeQuery()), 30));
        }
    }

And just to add more clarity to the example at hand, I add a simple implementation of the QueryComponent and QueryWrapper respectively, along with a concrete implementation of each.

C#
public abstract class QueryComponent<T>
{
    public abstract IQueryable<T> Query();
}

public class EmployeeQuery : QueryComponent<Employee>
{
    public override IQueryable<Employee> Query()
    {
        return new EmployeesRepository().GetAllEmployess().AsQueryable();
    }
}

public abstract class QueryWrapper<T> : QueryComponent<T>
{
    protected readonly QueryComponent<T> QueryComponent;
    protected QueryWrapper(QueryComponent<T> queryComponent)
    {
        QueryComponent = queryComponent;
    }

    protected IQueryable<T> CallTrailer()
    {
        return null != QueryComponent ? QueryComponent.Query() : null;
    }
}

public class ContracteesQuery : QueryWrapper<Employee>
{
    public ContracteesQuery(QueryComponent<Employee> queryComponent)
        : base(queryComponent)
    {
    }

    public override IQueryable<Employee> Query()
    {
        return CallTrailer().Where(p => p.IsContractee);
    }
}

Lastly, as with every post. I leave you with this quote by Christopher Alexander [^]:

"We are searching for some kind of harmony between two intangibles: a form which we have not yet designed and a context which we cannot properly describe."

Code well and Stay#!

Downloads

Here is a sample VS 2008 SP1 project that contains the query wrapper example. It is a console application that executes a sample query from the EmployeeQueryFactory.

History

  • 27 June 2010 - First version submitted
  • 28 June 2010 - Added source code example

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
Canada Canada
MCP, MCAD .NET

Blog: C# | B# | Stay#

Comments and Discussions

 
QuestionVery Nice. But what advantage does it have over the Specification pattern? Pin
Jacob Wakeem13-Jul-10 20:53
Jacob Wakeem13-Jul-10 20:53 
AnswerRe: Very Nice. But what advantage does it have over the Specification pattern? Pin
Anas Karkoukli15-Jul-10 3:18
Anas Karkoukli15-Jul-10 3:18 
GeneralMy vote of 4 Pin
achuki128-Jun-10 20:34
achuki128-Jun-10 20:34 
GeneralMy vote of 5 Pin
Sergio Romero28-Jun-10 10:07
Sergio Romero28-Jun-10 10:07 
I tried it out and works beautifully. This will make so much duplication of code dissapear from my project it's fantastic.
GeneralRe: My vote of 5 Pin
Avijit_Das28-Jun-10 13:36
Avijit_Das28-Jun-10 13:36 
GeneralRevision Reqd. Pin
Munim Abdul27-Jun-10 20:58
Munim Abdul27-Jun-10 20:58 
GeneralRe: Revision Reqd. Pin
Baconbutty28-Jun-10 4:43
Baconbutty28-Jun-10 4:43 
GeneralRe: Revision Reqd. Pin
Sergio Romero28-Jun-10 10:15
Sergio Romero28-Jun-10 10:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.