LINQ queries on non-generic collections

By | February 21, 2016

Introduction

LINQ (Language-Integrated Query) is a powerful tool of the .NET Framework. As the name implies, it integrates query capabilities directly into C# (or the .NET language of your choice). This article does not aim to be an introduction to LINQ, but rather how to use LINQ queries on non-generic collections (objects of IEnumerable type). If LINQ is completely unfamiliar to you, then I would recommend finding a tutorial on the web first and come back to this article later.

The standard LINQ query operators are restricted to generic collections (objects of IEnumerable<T> type). By using the IEnumerable extension method Cast<T> provided by LINQ we can easily create an IEnumerable<T>. Once we have an IEnumerable<T> we can perform LINQ queries. It is as simple as that. To solidify our understanding, let’s go through a couple of examples.

Example 1: LINQ query on a StringCollection object

private static void StringCollectionLinqQuery()
{
    var stringCollection = new StringCollection();
    stringCollection.Add("DuckDuckGo");
    stringCollection.Add("Google");
    stringCollection.Add("Bing");
    stringCollection.Add("Yahoo");
    stringCollection.Add("Ask");

    Console.WriteLine("-- ORIGINAL COLLECTION ORDER --");
    foreach (string item in stringCollection)
    {
        Console.WriteLine(item);
    }

    // Make it LINQ queryable by transforming the collection into an IEnumerable<T> 
    // using the Cast extension method
    IEnumerable<string> linqableCollection = stringCollection.Cast<string>();
    // Now we can easily order the collection using the power of LINQ
    List<string> orderedCollection = linqableCollection.OrderBy(item => item).ToList();
            
    Console.WriteLine("-- NEW COLLECTION ORDER --");
    orderedCollection.ForEach(item => Console.WriteLine(item));
}

So here we have a StringCollection object and we wish to alphabetically order the contents of the collection. We could write our own routine to reorder the list or we could use the OrderBy extension method provided by LINQ. Lets take the easy route and use the built-in power of LINQ. First, we create a LINQ query capable collection by using Cast<string> to create an IEnumerable<string>. Once we have obtained the generic collection, we order the collection using LINQ as we normally would.

Example 2: LINQ query on a DataTable object

To begin this example, I wrote a small setup script which initializes a DataTable object:

private static DataTable GenerateDataTable()
{
    var idDataColumn = new DataColumn("Id", typeof(int));
    idDataColumn.Caption = "Identifier";
    idDataColumn.ReadOnly = true;
    idDataColumn.AllowDBNull = false;
    idDataColumn.Unique = true;
    idDataColumn.AutoIncrement = true;
    idDataColumn.AutoIncrementSeed = 1;
    idDataColumn.AutoIncrementStep = 1;

    var nameDataColumn = new DataColumn("Name", typeof(string));
    var ageDataColumn = new DataColumn("Age", typeof(int));
    var sexDataColumn = new DataColumn("Sex", typeof(string));

    var petDataTable = new DataTable("Pet");
    petDataTable.Columns.AddRange(new DataColumn[] { 
        idDataColumn, nameDataColumn, ageDataColumn, sexDataColumn });

    var petDataRow = petDataTable.NewRow();
    petDataRow["Name"] = "Rex";
    petDataRow["Age"] = 6;
    petDataRow["Sex"] = "Male";
    petDataTable.Rows.Add(petDataRow);

    petDataRow = petDataTable.NewRow();
    petDataRow["Name"] = "Rambo";
    petDataRow["Age"] = 2;
    petDataRow["Sex"] = "Male";
    petDataTable.Rows.Add(petDataRow);

    petDataRow = petDataTable.NewRow();
    petDataRow["Name"] = "Luna";
    petDataRow["Age"] = 13;
    petDataRow["Sex"] = "Female";
    petDataTable.Rows.Add(petDataRow);

    petDataRow = petDataTable.NewRow();
    petDataRow["Name"] = "Lovey";
    petDataRow["Age"] = 8;
    petDataRow["Sex"] = "Female";
    petDataTable.Rows.Add(petDataRow);

    return petDataTable;
}

Here is the code which performs LINQ queries over the DataTable:

private static void PetDataTableLinqQuery()
{
    DataTable petDataTable = GenerateDataTable();

    // Similarly to the StringCollection we can use the Cast extension method
    // to obtain an IEnumerable<T> so we have a LINQ queryable object
    IEnumerable<DataRow> dataRows = petDataTable.Rows.Cast<DataRow>();

    // Alternatively we could have used AsEnumerable on a DataTable
    // But I wish to show a "general" approach in obtaining a 
    // LINQ queryable (IEnumerable<T>) object
    // IEnumerable<DataRow> dataRows = petDataTable.AsEnumerable();

    Console.WriteLine("-- All Data Rows --");
    PrintPetRows(dataRows);

    IEnumerable<DataRow> maleDataRows = dataRows
        .Where(dr => string.Equals((string)dr["Sex"], "Male", StringComparison.OrdinalIgnoreCase));
    Console.WriteLine("-- Male Only Data Rows --");
    PrintPetRows(maleDataRows);

    IEnumerable<DataRow> olderThan5DataRows = dataRows
        .Where(dr => (int)dr["Age"] > 5);
    Console.WriteLine("-- Older Than 5 Data Rows --");
    PrintPetRows(olderThan5DataRows);

    IEnumerable<DataRow> orderedDataRows = dataRows
        .OrderBy(dr => (string)dr["Name"]);
    Console.WriteLine("-- Ordered By Name Data Rows --");
    PrintPetRows(orderedDataRows);
}

private static void PrintPetRows(IEnumerable<DataRow> dataRows)
{
    foreach (DataRow dataRow in dataRows)
    {
        Console.WriteLine("ID: {0}; NAME: {1}; AGE: {2}; SEX: {3}", 
            dataRow["Id"], dataRow["Name"], dataRow["Age"], dataRow["Sex"]);
    }
}

Here the same approach is used in order to obtain an object which we can perform LINQ queries on. Alternatively, DataTable supports the AsEnumerable method which will return an IEnumerable<DataRow> from a DataTable. I have shown here how Cast<DataRow> can be used instead in order to showcase a more general approach to solving the problem. Again, once we have an IEnumerable<DataRow> we can query the data source using the standard query operators of LINQ.

I hope you found this article helpful. Note that I have exclusively used the method-based query syntax in this article, but query expression syntax could be used instead and everything would be equally valid. I have used the method-based query syntax here out of personal preference and nothing more.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.