Sunday, May 18, 2008

Using Linq to sort and filter entity lists

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/using_linq_to_sort_and_filter_entity_lists.htm]

One of the big new features for .Net if LINQ - Language Integrated Query. There's lots of good tutorials out there - you can see 101 examples, download a free GUI editor from the guys who wrote C# 3.0 in a Nutshell, or even just google it. As I was toying with Linq (via the very good chapters from C# 3.0 in a Nutshell), I especially enjoyed using Linq to query objects. Here's the test snippets I was running.

 

First, I created a trivial entity:

 

    public class Employee
    {
      public Employee(int age, string strFirstname)
      {
        this.Age = age;
        this.FirstName = strFirstname;
      }
      public int Age { get; set; }
      public string FirstName { get; set; }

      public override string ToString()
      {
        return this.FirstName + " (" + this.Age + ")";
      }
    }

 

Then I set up an MSTest project (perfect for stubbing out things like this), and added this initializer to always give me an Employee array:

 

    [TestInitialize]
    public void SetData()
    {
      _employees = new Employee[]
      {
        new Employee(39, "Homer"),
        new Employee(39, "Marge"),
        new Employee(7, "Lisa"),
        new Employee(9, "Bart"),
        new Employee(3, "Maggie")
      };
    }

    Employee[] _employees;

 

Now, I can write some test snippets.

 

Filter an Entity list

 

Given an array of Employees, I can filter them by business rules, such as getting all employees over 10 years old. We used to need to handle this (in C#) by writing a custom loop that checked each item. Now, Linq gives us a Domain-Specific-Language to just handle this, usually with 1 line of code.

 

Note the "n => n.Age < 10)", this is where the syntax may seem new. This is where you can put your appropriate filter expression. Linq then provides a ToArray method to ensure that an array of employees are returned - as opposed to some differently type dynamically-crated object.

 

    [TestMethod]
    public void FilterEntity()
    {
      Employee[] ae = _employees.Where(n => n.Age < 10).ToArray();
      Assert.AreEqual(3, ae.Length);
    }

 

This alone is great. There's no fluff wasted. It's a single line, and every keyword and expression maps to a specific concept: "Given an array of employees, filter them where the age < 10, and then return an array of employees."

 

It gets better - the filter expression can contain your own custom method. In this case, I created a "HasBigName" method to check for custom logic (the length of the string).

 

    [TestMethod]
    public void FilterEntitySpecial()
    {
      Employee[] ae = _employees.Where(n => HasBigName(n.FirstName)).ToArray();
      Assert.AreEqual(3, ae.Length);
    }

    public bool HasBigName(string s)
    {
      if (s == null || s.Length <= 4)
        return false;
      else
        return true;
    }

 

Sort an Entity list

Linq makes it easy to sort entities by their fields. In this case, I can sort the employees by their first name.

 

    [TestMethod]
    public void SortEntity()
    {
      Employee[] ae = _employees.OrderBy(n => n.FirstName).ToArray();
      Assert.AreEqual(5, ae.Length);
      Assert.AreEqual("Bart", ae[0].FirstName);
    }

 

Conclusion

Linq is a great example of something that was possible to do before, but wasn't always practical. Developers don't like writing  extra lines of plumbing code just to do mundane things like sorting and filtering, so it's a huge win to have a means to quickly handle that.

No comments:

Post a Comment