Demystifying The Code

Patterns-Based Silverlight Development – Part III – Pipeline Pattern

Introduction

In yesterday’s post, I build our Repository interface and the implementation, as well as added some server-side validation, following a simple pattern.  I also added a test project and wrote some tests to test the our validation logic.  In this post I will implement the Pipeline pattern.  I will then implement a fake repository and use it to test our Pipeline.

 

Acknowledgements

Most of what you will see in this post follows very closely with what Rob Connery outlined in his MVC Storefront series.  I am, however, not using TDD, as that is not the goal of this series.  Again, I would highly recommend following his series if you haven’t already.

 

The Pipeline Pattern

“In software engineering, a pipeline consists of a chain of processing elements …, arranged so that the output of each element is the input of the next.” – Wikipedia – Pipeline (software)

Whether we know it or not, we have all used pipelines.  Perhaps the best examples are WCF or IIS.  The description above from Wikipedia (the purveyor of all knowledge) hit the most salient points on the head.  You have a chain of processing elements.  Each element takes an input and has an output.  The output of one element is the input of another.  In a typical pipeline, each element is designed to take in an input of a specific type, perform some operation on it, and return the same type as the output.

 

IQueryable<T> Overview

The following comes straight from the online documentation for IQueryable<T> and are quite illuminating. 

image

The Expression property returns the expression tree that is associated with the IQueryable instance.  When you do things like add a ‘where’ or a ‘select’ or ‘order by’ (the list goes on …) to your query, it is stored in the expression tree.  This in-memory representation of your query is the key enabler for allowing deferred execution.  What this means is that we can continually add to the query (the expression tree) up until the time we execute it.  The query is not executed until we enumerate over the results. 

Let’s take a look at a simple example that will help illustrate this.  Looking back to our repository, we implemented only one fetch operation for tickets.  It looks like this:

public IQueryable<Ticket> GetTickets()
{
    return db.Tickets;
}

You might have expected multiple fetch operations like GetTicketsBySeverityLevel or GetTicketsByPage.  This is how we typically built this layer in the past.  The challenge with this approach is that any time you wanted to return a different slice of data, you had to go back and add another operation to the Repository.  With IQueryable<T>, we can simply call GetTickets and add to the query to suit our needs.  So instead of implementing a GetTicketsBySeverityLevel in the Repository, we could do the following:

//Get the IQueryable<T> from the repository 
TicketRepository rep = new TicketRepository();
IQueryable<Ticket> query = rep.GetTickets();

//Add a where clause (by severity level)
query = query.Where(t => t.SeverityLevelID == 1);

//Fetch the data
List<Ticket> tickets = query.ToList();

We are going to take advantage of this in implementing our Pipeline.  The other enabling technology we are going to be taking advantage of is extension methods.  I’ll cover that now…

 

Extension Methods Overview

Extension methods let you extend an existing type without actually deriving from it.  At first glance, this raises some red flags surrounding security, but an extension method only has access to the public members of the type it is extending.  This is no different than any other code that may operate on this type.  So what is an extension method really?  It is a pre-compile syntax that, among other things, enables LINQ and allows you to create more readable and maintainable code.  Let’s take a look at an example to illustrate the readability/maintainability part.  Let’s assume that I want to expose the functionality that will translate English to Jive.  Obviously, this will operate on a string.  The old-timey way to do this would to be to write a static method and consume it  like so:

//Traditional Static Method
public static string TranslateToJive(string text)
{
    return translateToJive(text);
}
//Call our traditional static
string greeting = "Relax";
Console.WriteLine(StringExtensions.TranslateToJive(greeting));

We can easily implement this functionality as an Extension Method.  Extension Methods are public static (they have to be both public and static) methods in a static class.  What transforms a static method into an extension method is prepending the ‘this’ keyword to the first method parameter.  Whatever the type of that parameter is the type we will be extending.  For example, we could re-write our earlier example like this:

//Extension method, extending string
public static string ToJive(this string text)
{
    return translateToJive(text);
}
//Call our extension method
string greeting = "Relax";
Console.WriteLine(greeting.ToJive());

While both bits of code will output the same thing to the console: “Jes hang loose”, as you can see, the code with the extension method is simpler to read.  In this simple example, you may not see a great increase in readability, but you will see it more clearly when we get into our pipeline example.  Another great benefit of the extension method is that it provides intellisense support.  We do not need to know about the existence of a TranslateToJive method that takes a string.  We simply press ‘.’ after our string and we see:

image

 

Building our Pipeline

Now let’s tie all of the concepts brought out in this post together by implementing our pipeline.  Our pipeline will be a bunch of extension methods that extend IQueryable<Ticket>.  These extension methods will take in an IQueryable<Ticket> (or in this case extend it, logically the same thing) and return IQueryable<Ticket>.  As we will see, we will be able to string a bunch of these extension methods together.

There will be some extension methods that don’t return IQueryable<Ticket>, perhaps just returning a single ticket.  An example would be a “ByTicketId” extension method.  Methods that do not return IQueryable<T> will end that pipeline. 

Another name for the Pipeline pattern is ‘Pipes and Filters’.  That name explains exactly what we want to do.  We want to create a group of filters that can be applied to Tickets.  Let’s create our Ticket filters.

  1. Create a TicketFilters static class in HelpDesk.Data
    • Right-Click HelpDesk.Data –> Add –> Class
    • Name it TicketFilters.cs (I put mine in a Filters folder)
    • Mark the class public and static
  2. Add the following extension methods
public static class TicketFilters
{
    public static IQueryable<Ticket> BySeverityLevelID(
        this IQueryable<Ticket> query,
        int id)
    {
        return query.Where(q => q.SeverityLevelID == id);
    }

    public static IQueryable<Ticket> ByPage(
        this IQueryable<Ticket> query,
        int pageNumber,
        int pageSize)
    {
        int skip = (pageNumber) * pageSize;

        return query.Skip(skip).Take(pageSize);
    }

    public static Ticket ByTicketID(this IQueryable<Ticket> query,
        int id)
    {
        return query.SingleOrDefault(q => q.TicketID == id);
    }
}

 

Does the Pipeline Increase Readability?

Consider this scenario.  You want to get the second page of 10 Tickets with a SeverityLevel of 1.  Here is how you would do this without our extension methods:

var tickets = repository.GetTickets()
    .Where(t => t.SeverityLevelID == 1)
    .Skip(10)
    .Take(10);

versus this with our extension methods:

var tickets = rep.GetTickets().BySeverityLevelID(1).ByPage(2, 10);

Quite a difference (in my opinion).

 

Testing our Pipeline

When I wrote the tests against my validation logic, I was able to simply cruft up a Ticket object and set whatever properties I needed to set (or not set).  Here, however, I will need to call into my Repository.  But do I really want to do that?  Take the ByTicketID filter.  It takes in an integer like 1.  If we wrote our test calling this filter with a 1 and it failed it may be because there was no record in the database with an ID of 1.  This may lead to a false negative.  Our code actually worked as designed, the test failed because of what was in the database (or rather wasn’t). 

Couldn’t we write some test precondition that checks to see if there is a record with ID = 1 in the database, if not, insert it?  Yes, we could.  But then, to be good citizens, we would probably want to add some code to remove the newly inserted record.  We really don’t want to be in the business of managing our test database.  We want to test our logic.

For this reason, what we might want to do is to use a fake or mock repository.  Rhino Mocks is a popular framework that provides this functionality (and there are many other great tools).  However, for the purpose of this post, I am going to simply implement my own fake repository.  In future posts, I may replace that with Rhino Mocks or another solution.

Add The Fake Ticket Repository

  1. Right-click the HelpDesk.Server.Tests project –> Add –> New Folder –> Name it Fakes
  2. Right-click the Fakes folder –> Add –> Class
  3. Name it FakeTicketRepository.cs
  4. Mark it public
  5. Add the following code:
public class FakeTicketRepository : ITicketRepository
{
    private List<Ticket> ticketList;
    private List<SeverityLevel> severityLevelList;

    public FakeTicketRepository(List<Ticket> tickets,
        List<SeverityLevel> severityLevels)
    {
        ticketList = tickets;
        severityLevelList = severityLevels;
    }

    public IQueryable<Ticket> GetTickets()
    {
        return ticketList.AsQueryable();
    }

    public IQueryable<SeverityLevel> GetSeverityLevels()
    {
        return severityLevelList.AsQueryable();
    }

    public void Add(Ticket ticket)
    {
        ticketList.Add(ticket);
    }

    public void Delete(Ticket ticket)
    {
        ticketList.Remove(ticket);
    }

    public void Save()
    {
        foreach (Ticket ticket in ticketList)
        {
            if (!ticket.IsValid)
                throw new ValidationException("Rule violations");
        }

        return;
    }

    #region Helper Static Methods
    public static FakeTicketRepository CreateFakeTicketRepository()
    {
        var repository = new FakeTicketRepository(CreateTestTickets(), CreateTestSeverityLevels());
        return repository;
    }

    public static List<SeverityLevel> CreateTestSeverityLevels()
    {
        List<SeverityLevel> slevels = new List<SeverityLevel>();

        slevels.Add(new SeverityLevel() { SeverityLevelID = 1, Level = "High" });
        slevels.Add(new SeverityLevel() { SeverityLevelID = 2, Level = "Medium" });
        slevels.Add(new SeverityLevel() { SeverityLevelID = 3, Level = "Low" });

        return slevels;
    }

    public static List<Ticket> CreateTestTickets()
    {
        List<Ticket> tickets = new List<Ticket>();
        for (int i = 0; i < 10; i++)
        {
            Ticket sampleTicket = new Ticket()
            {
                TicketID = i,
                ShortDescription = "Short Description " + i.ToString(),
                LongDescription = "Long Description " + i.ToString(),
                TicketDate = DateTime.Now.AddDays(i * -1),
                IsOpen = (i % 2 == 0),
                SeverityLevelID = 1,
                SeverityLevel = CreateTestSeverityLevels()[0]
            };

            tickets.Add(sampleTicket);
        }
        return tickets;
    }
    #endregion
}

What you should gather from this code is the following:

  • I am using generic Lists (List<T>) to store my fake ticket and severity level data
  • I overloaded the constructor, forcing you to pass in the fake Ticket and SeverityLevel lists
  • I implemented IRepository.  All of the IRepository implementations operate on the local Lists
  • I created a helper static method CreateFakeTicketRepository that will return a FakeTicketRepository with fake data in it.  If you don’t feel like creating your own fake data and passing it in, you can use this out-of-the-box fake data.

Some Pipeline Tests

[TestMethod]
public void Passing_ByTicketID_Filter_1_Returns_The_Appropriate_Ticket()
{
    ITicketRepository rep =
        Fakes.FakeTicketRepository.CreateFakeTicketRepository();

    Ticket ticket = rep.GetTickets()
                    .ByTicketID(1);

    Assert.IsTrue(ticket.TicketID == 1);
}

[TestMethod]
public void Passing_ByPage_Filter_Page_1_PageSize_3_Should_Return_Tickets_With_IDS_3_4_5()
{
    ITicketRepository rep =
        Fakes.FakeTicketRepository.CreateFakeTicketRepository();

    var tickets = rep.GetTickets().ByPage(1, 3).ToList();

    Assert.IsTrue(tickets[0].TicketID == 3 &&
        tickets[1].TicketID == 4 &&
        tickets[2].TicketID == 5);

}

You can see from the above code that I am using my fake ticket repository.  I am then testing some of my filters.  Because I know what the test data looks like, I am assured that I am testing my filter logic and not my data.

 

Sample Code

You can get the sample code for implementing the pipeline here.  (You will need to be a registered user of the site to get the code)

What’s Next

In the next post, I will implement the WCF Service layer. 

Comments

One Response to “Patterns-Based Silverlight Development – Part III – Pipeline Pattern”

Trackbacks

Check out what others are saying about this post...
  1. [...] Patterns-Based Silverlight Development – Part III – Pipeline Pattern [...]



Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!

Demystifying The Code