LINQ Programming: Language Integrated Query

This article exlains the core features of LINQ for beginners.

Article Overview

  • Introduction
  • Traditional Queries
  • Role of LINQ
  • LINQ Basic
  • Query Operators
  • Summary

Introduction

Language-Integrated Query (LINQ) is one of the most attractive innovations of .NET 3.5 versions. It can be used equally well in a range of .NET applications, for instance console applications to rich Windows clients. Although you can combine LINQ to anywhere in an ASP.NET application you are most likely to use LINQ as a part of database components. LINQ basically replaces the hassle of ADO.NET. This chapter introduces you to the LINQ model and its role in the .NET platform. Here you learn the role of query operations and query operators. You will see how to create LINQ query expressions that can select, sort and group, alter and transform data. Apart from LINQ, you will also learn other significant concepts related to LINQ, like extension methods, anonymous methods and lambda expressions.

Traditional Queries

Before delving into the features of LINQ, let's have a glimpse at how queries are done todays. What kind of ADO.Net business logic is implemented in the database access layer to manipulate data from a database. The following sample performs some database related operations such as extracting data from a table over the front-end Windows form. The database Master contains a table tbl_Emp with some columns as shown below.

Emp design
                              Figure 1.1: tbl_Emp design

To make it easier to shuffle information from the Master database back and forth, it makes sense to create an empDetails class that provides all the database fields and properties. Here the full code of that class is as in the following.

Project: LINQsample

  1. Public class empDetails  
  2. {  
  3.    public empDetails() { }  
  4.   
  5.    public string FirstName { getset; }  
  6.    public string LastName { getset; }  
  7.    public string Country { getset; }  
  8.   
  9.    public empDetails(string firstName, string lastName,  
  10.    string country)  
  11.    {  
  12.   
  13.       this.FirstName = firstName;  
  14.       this.LastName = lastName;  
  15.       this.Country = country;  
  16.    }  
  17. }  
The following empOperation class performs the actual operation to extract data from the database table tbl_Emp. It initializes the database query string into its constructor to establish a connection with the database. To access the database table, various classes from the namespace System.Data.SqlClient are needed. First of all a connection is opened with the SqlConnection class. The SqlCommand class defines the SQL query statements, after successfully opening the connection. The data is read using the SqlDataReader class, that reads records row by row. Finally every selected column is stored into a generic class empDetails of type object employees.

Project: LINQsample

  1. public class empOperation  
  2. {  
  3.    private string connectionString;  
  4.   
  5.    public empOperation()  
  6.    {  
  7.       // Get connection string from web.config.  
  8.       connectionString = @"Data Source=localhost;Initial Catalog=Master;Integrated Security=SSPI";  
  9.    }  
  10.   
  11.    public List<empDetails> GetEmployees()  
  12.    {  
  13.       SqlConnection con = new SqlConnection(connectionString);  
  14.       string query = @"select * from tbl_Emp";  
  15.       SqlCommand cmd = new SqlCommand(query, con);  
  16.   
  17.   
  18.       // Create a collection for all the employee records.  
  19.       List<empDetails> employees = new List<empDetails>();  
  20.   
  21.       try  
  22.       {  
  23.          con.Open();  
  24.          SqlDataReader reader = cmd.ExecuteReader();  
  25.   
  26.          while (reader.Read())  
  27.          {  
  28.             empDetails emp = new empDetails((string)reader["FirstName"],  
  29.             (string)reader["LastName"], (string)reader["Country"]);  
  30.             employees.Add(emp);  
  31.          }  
  32.          reader.Close();  
  33.   
  34.          return employees;  
  35.       }  
  36.       catch (SqlException err)  
  37.       {   
  38.          throw new ApplicationException("Data error.");  
  39.       }  
  40.       finally  
  41.       {  
  42.          con.Close();  
  43.       }  
  44.    }  
  45.   
  46. }  
Finally in the presentation layer (Windows forms) we place a DataGridView control to display the entire extracted row from the database. Here we call the helper class empOperation method and assign its reference to the generic type of an empDetails class object. Therefore we added all the returned rows into the generic object and pass this reference to the datagrid control to populate the rows.

Project: LINQsample
  1. private void btnGetData_Click(object sender, EventArgs e)  
  2. {  
  3.    empOperation obj = new empOperation();  
  4.   
  5.    List<empDetails> employees = obj.GetEmployees();  
  6.   
  7.    List<empDetails> lEmp = new List<empDetails>();  
  8.    foreach (empDetails emp in employees)  
  9.    {  
  10.       lEmp.Add(emp);  
  11.    }  
  12.   
  13.    dbvData.DataSource = lEmp;  
  14. }  
Finally after successfully compiling the program, the moment the user clicks the button, the data grid populates all the rows of the table as in the following:

Output
                                             Figure 1.2: Output

So the objective of the previously mentioned sample is to show the complexities behind the traditional database related operations. The extra overhead of implementing ADO.NET functionality is to perform query executions. You need to choose 2 ways to access the diverse kind of data sources such as XML, list of database in traditional database operations. But with LINQ, you get one way to access all these data sources.

Role of LINQ

Data-acquisition languages such as SQL are functional in nature, meaning that the emphasis is placed on the operation and there is no immutable data used during the process. LINQ bridges the gap between the imperative programming style and the functional programming style. LINQ lightens the programmer's job allowing them to focus on the business logic while spending less time coding associated with data access code. If you have noticed earlier during building the sample program, think about how often you have found yourself coding up the same type of boilerplate code over and over again. LINQ removes some of that burden.

ADO.NET provides an API to get access to relational databases and to represent relational data in memory. This API consists of classes like SqlConnection, SqlDataAdapter, DataSet and so on. The problem with these classes is that they force the developer to work explicitly with tables, records and columns whereas C# uses OOP.

As a programmer, it is hard to deny that the vast majority of our programming time is spent obtaining and manipulating data from numerous places. Prior to .NET 3.5, interacting with a specific flavor of data required programmers to use diverse APIs. Consider, for example, the following table that illustrates several common APIs used to access various types of data.

Type of Data Data Source

There is nothing wrong with these approaches to data manipulation. In fact, when programming with .NET 3.5, you can certainly make direct use of ADO.NET, the XML namespaces and the various collection types. However, the basic problem is that each of these APIs are complicated that offer very little in the way of integration.

LINQ Basic

LINQ query expressions look like SQL expressions. The LINQ query expression (unlike a traditional SQL statement) is strongly typed. Therefore, the C# compiler ensures that these expressions are syntactically well formed. Query expressions have metadata representations within the assembly that makes use of them. Tools such as Visual Studio 2010 IDE can use this metadata for useful features such as IntelliSense, auto completion and so forth.

LINQ Sample

To begin examining the LINQ programming model, let's build simple query expressions to manipulate data contained within various arrays. Create a .NET 4.0 Console Application and define a string array containing six or so items of your choice. When you have any array of data, it is very common to extract a subset of items based on certain requirements. You could certainly perform such tasks using members of the System.Array type that is a bit cumbersome but LINQ query expressions can greatly simplify the process. In this sample, we wish to obtain a subset from the array that contains items with names consisting of more than eight characters.

Project: LinqTest 
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4.   
  5. namespace linqTest  
  6. {  
  7.     class Program  
  8.     {  
  9.         static void Main(string[] args)  
  10.         {  
  11.             // array of strings.  
  12.             string[] arVar = {"Database""Networking",  
  13.                                "Programming""Amazon",  
  14.                                 "box""System"};  
  15.   
  16.             // Build a query expression to represent the items in the array  
  17.             // that have more than 8 letters.  
  18.             IEnumerable<string> obj = from a in arVar  
  19.                                          where a.Length > 8  
  20.                                          orderby a  
  21.                                          select a;  
  22.             // Print out the results.  
  23.             foreach (string x in obj)  
  24.                 Console.WriteLine("Item: {0}", x);  
  25.   
  26.             Console.ReadLine();    
  27.         }  
  28.     }  
  29. }  
Notice that the query expression created here, makes use of the from, in, where, orderby and select query operators. We will dig into the formalities of query expression syntax later. Here each item that matches the search criteria has been given the name “a” and the results are stored in the object that implements the generic version of IEnumerable<T>. Once we obtain the result set, we then simply print out each item using a standard foreach construct. The output of this sample program is as in the following:

Program output
                                                                  Figure 1.3: output

LINQ Queries

If we took the reference of our earlier project LINQsample, we can also implement the LINQ functionality there. Suppose we are retrieving a result set from the database table tbl_Emp where the employees have last names starting with the letter "Y". You can carry on this operation in a loop through the full collection of employees with a condition check as in the following:
  1. foreach (empDetails emp in employees)  
  2. {  
  3.    if (emp.LastName.StartsWith("Y"))  
  4.    {  
  5.       lEmp.Add(emp);  
  6.    }  
  7. }  
You can do the same task using a LINQ expression. The following modification shows how to rewrite the code, replacing the foreach block with a LINQ query. The following program also shows the filtering sorting over a specific data set. You will see how a where clause can filter the results to include only those that match a specific condition.
  1. empOperation obj = new empOperation();  
  2. List<empDetails> emp = obj.GetEmployees();  
  3.   
  4. IEnumerable<empDetails> data = from x in emp  
  5. where x.LastName.StartsWith("Y")  
  6. select x;  
  7.   
  8. dataGrid1.DataSource = data.ToList();  
Note: LINQ queries are fully name and type-checked at compile-time, reducing runtime error surprises.

Before proceding with LINQ, you need to understand how a LINQ expression is composed. All LINQ expressions must have the from clause that indicates the data source and a select clause that indicates the data that you want to retrieve the from clause, as in the following:

from x in employees

The from clause identifiers has two parts of information. The word immediately after from assigns an alias that represents individual items in the data source and the word after in identifies the data source. In this case employees that hold the empDetails instance. LINQ expressions work and return on objects that implement IEnumerable<T>. Thus, you can pass the result from one LINQ expression into other expressions.

You can retrieve subsets of results by implementing the projections. For example, you could pull out a list of First Name and Last Name in concatenated form as in the following:
  1. empOperation obj = new empOperation();  
  2. List<empDetails> emp = obj.GetEmployees();  
  3.   
  4. var data = from x in emp  
  5. select x.FirstName + x.LastName;  
  6.   
  7. foreach (var a in data)  
  8. {  
  9.    textBox1.AppendText(a + "\n");  
  10. }  
Note: The var keyword is resolved at compile time and can't be used as a class member variable.

You'll also need to use the var keyword whenever you want to reference an individual object. The previously mentioned sample produces the following result:

concatenation
                  Figure 1.4: concatenation

Traditionally, working with collections of objects meant writing lots of looping code using for or foreach loop to iterate through a collection, carrying out filtering using if statements. LINQ frees you from having to write looping code; it allows you to write queries that filter a list or calculate aggregate functions on elements. The following example shows the basic query syntax to understand it in a more granular fashion.
  1. int [] data = new int[] {5,3,6,8,4,9,0,1,2};  
  2.   
  3. var res = from x in data  
  4.           where x < 6  
  5.           orderby x  
  6.           select x;  
  7.   
  8. foreach (int a in res)  
  9. {  
  10.     textBox1.AppendText(a + "\n");  
  11. }  
Query Operators

LINQ is built upon the use of standard query operators, that are methods that operate on sequences such as collections that implement IEnumerable or IQueryable. The LINQ query defines a declarative syntax for most common operators as in the following tables.

Operators Description

Summary

This article posed the contents for beginners. Here, you have learned the core features of LINQ. You also saw that LINQ has a wide range of potential applications that provide a declarative model for retrieving and processing data that allows you to use the same syntax with a wide range of various types of data. This article illustrated why LINQ is important and examined the LINQ common query operators such as Select, Where, Join and so on. A forthcoming article of this series shows advance features of LINQ such as Extension Methods, Lambda Expressions and LINQ to XML and so on.