The .NET Language Integrated Query (LINQ) Framework

LINQ (Language Integrated Query) is a recent powerful addition to the .NET framework and allows you to query your data sources in an elegant and simple way. In this post I’ll give a very brief introduction to LINQ and how it makes your code much more readable.
Suppose that we have an object defined as follows:

// lang cpp
class DataItem
{
    public DataItem(int id, string name)
    {
        ID = id;
        Name = name;
    }
    public int ID { get; set; }
    public string Name { get; set; }
}

It’s a simple object with two properties. The above class definition also shows a new feature for quickly defining properties. You don’t need to write the get/set implementation anymore and you don’t need a private backing variable anymore. Before, you had to write something like:

// lang cpp
private string name;
public string Name
{
    get { return name; }
    set { name = value; }
}

This can all be replaced with the following 1 line of code:

// lang cpp
public string Name { get; set; }

Back to our example. Let us define a small array with some of our objects.

// lang cpp
DataItem[] items = {new DataItem(10,"string 10"),
                new DataItem(9,"string 9"),
                new DataItem(8,"string 8"),
                new DataItem(7,"string 7"),
                new DataItem(6,"string 6 test"),
                new DataItem(5,"string 5"),
                new DataItem(4,"string 4 (test)"),
                new DataItem(3,"string 3"),
                new DataItem(2,"string 2"),
                new DataItem(1,"string 1")};

We will now write code to find all items that contain “test” in the name and then order those items according to the ID. The following code shows how this could be done in the pre-LINQ world.

// lang cpp
// Our custom sort predicate to be used by the List<T>.sort() function.
class DataItemSorter : IComparer<DataItem>
{
    public int Compare(DataItem x, DataItem y)
    {
        return x.ID < y.ID ? 1 : 0;
    }
}

class Program
{
    // Function to determine if an item is a match.
    // For this example, it's a simple string match.
    static bool MatchCondition(DataItem item)
    {
        return item.Name.Contains("test");
    }

    static void Main(string[] args)
    {
        // Define our items, see above.
        DataItem[] items = {new DataItem(10,"string 10"), ...

        // Get all matches and store them in a temporary list
        List<DataItem> matchingItems = new List<DataItem>();
        foreach (DataItem item in items)
        {
            if (MatchCondition(item))
                matchingItems.Add(item);
        }

        // Sort items according to ID
        matchingItems.Sort(new DataItemSorter());

        // Print matching items
        foreach (DataItem item in matchingItems)
                Console.WriteLine(item.Name);
    }
}

As you can see in the code above, we need to write a custom class implementing IComparer<T> to be able to sort our results. Then we need to create a temporary list holding our matches which we can then sort. All in all this is quite a lot of code for something that is conceptually a simple thing. The code is also not so clear and is jumping around a bit.
Now, lets write the same thing using LINQ.

// lang cpp
class Program
{
    static void Main(string[] args)
    {
        // Define our items, see above.
        DataItem[] items = {new DataItem(10,"string 10"), ...

        IEnumerable<string> query = from item in items
                        where item.Name.Contains("test")
                        orderby item.ID
                        select item.Name;
        foreach (string item in query)
            Console.WriteLine(item);
    }
}

The code is much simpler and much more readable. No need for an IComparer implementation, no need for a match function, no need for a temporary list. Basically it accurately maps “what we want to do” to “how we do it”. It creates a “query” that will select all items containing “test” in the name, orders the results according to ID and returns the Name of those objects. The foreach then iterates over the results and writes out the matches.
This can be simplyfied even more as follows.

// lang cpp
class Program
{
    static void Main(string[] args)
    {
        // Define our items, see above.
        DataItem[] items = {new DataItem(10,"string 10"), ...

        var query = from item in items
                        where item.Name.Contains("test")
                        orderby item.ID
                        select item.Name;
        foreach (var item in query)
            Console.WriteLine(item);
    }
}

Now we are using a new feature of the .NET 3.5 framework which is “var”. When you use this, the compiler will figure out the type of the variable automatically based on the expression on the right.

The “select” statement can also return whole objects instead of just strings. For example, let us rewrite the query so that it returns an object with 3 member variables: MyName, MyNewID, MyFixedVariable.

// lang cpp
var query = from item in items
            where item.Name.Contains("test")
            orderby item.ID
            select new {
                    MyName = item.Name,
                    MyNewID = item.ID + 10,
                    MyFixedVariable = 9};
foreach (var item in query)
{
    Console.WriteLine("MyName={0}; MyNewID={1}; MyFixedVariable={2}",
            item.MyName, item.MyNewID, item.MyFixedVariable);
}

This query creates a new anonymous object with our 3 member variables. While writing this code in Visual Studio 2008, the IDE has complete IntelliSense in the foreach loop, meaning that when you type “item.” it will correctly show the 3 member variables of our anonymous type.

Another important thing to remember about LINQ is that the query is only executed at the time you start enumerating the results. So, in our example the query is only executed once we reach the foreach statement. This allows you to construct your query in several steps.

My example above used LINQ to query data in standard containers, which is called LINQ To Objects. Besides this there is also LINQ To Datasets, LINQ To SQL, LINQ To Entities and LINQ To XML. It is also possible to write your own providers for LINQ. In my example above, if you would have a database with the dataitems instead of the standard array, you could still use the same LINQ query statement, except you need to replace the “items” in the “from” clause to your IEnumerable database table. That’s all that needs to be changed.

I hope this very brief introduction to LINQ gives you an idea about the power of it. If you want to learn more about the possibilities of LINQ you can get more information with the following resources.

Share

Leave a Comment

Name: (Required)

E-mail: (Required)

Website:

Comment: