The focus of this article will be an introduction to Language Integrated Query, or LINQ for short. This article will cover what LINQ is and isn’t, with a brief refresher on the language features that enable it, and then jump in to some examples of working with LINQ. This will be part one of a couple of articles that cover LINQ in some detail.
What is Language INtegrated Query?
Many of my past articles, even all the way back to some of my first articles such as Database Independent Data Access, have had something to do with accessing and manipulating data. Commonly, data is stored in a database, but there are many other forms of accessing and manipulating data as well such as data files, event logs, the Registry, and so forth. Querying and manipulating data is a common part of many applications.
LINQ (often overheard pronounced as “link”) is a step in the evolution of data access. It is a programming model that brings a much needed uniformity to accessing data from files, XML, database, registry, event log, and a whole host of other sources of data such as Active Directory and even services from 3rd parties such as Flickr. It is designed to work with all shapes and sizes of different data and allow you to perform Query, Set, and Transform operations on all of it. Pretty much anything that implements IEnumerable is a target for LINQ.
What Is LINQ Not?
It can be just as useful when trying to understand something to look at the counter of what it is, which is what it is not. One of the most common reactions I’ve heard about when people are first introduced to LINQ is that it is just embedded SQL, which it is not. Although LINQ syntax can be SQL-like in many of its forms, it is not just embedded SQL and is not limited to just querying databases. LINQ is not automatically supported by all .NET languages. There were no modifications to the Common Language Runtime (CLR). All of the modifications were made to the languages and their respective compilers. It requires some language-specific extensions. Visual Basic .NET 9.0 and C# 3.0 both have integrated language support for LINQ.
Language Features that Enable LINQ
LINQ makes heavy use of Generics. Additionally, there were a number of features added to the Visual Basic and C# languages specifically to support LINQ. A couple of my more recent articles introduced some of these language features as a precursor to LINQ. The following contains a partial list of the language features that help enable LINQ and a brief description of each:
- Type inference: Shorthand indicating the variables type is the compile time type of the right hand assignment
- Extension Methods: Extending an existing value or reference type without deriving a new type
- Object initializer: Short form of object initialization syntax that generates the equivalent code
- Anonymous types: Create statements without constructing a method or type
- Lambda expressions: Concise way of creating inline methods
- Query expressions: SQL-like statements within code for manipulating objects
I’m certain each of these languages features adds some benefit on their own, but I’ve not personally found reason yet to use many of them outside of LINQ.
Flavors of LINQ
There are a variety of flavors of LINQ for accessing and manipulating different data sources. The trailing list contains some of the data domains provided by Microsoft. Some of these will be topics of future .NET Nuts and Bolts articles.
- LINQ to Objects: Manipulates collections of objects
- LINQ to DataSets: Manipulates a DataSet using LINQ
- LINQ to SQL: Maps between custom types and a physical database table schema
- LINQ to Entities: Uses a conceptual Entity Data Model to create a conceptual model of a physical database
- LINQ to XML: Allows querying and manipulation of XML
Introduction to LINQ Syntax
For those who are very particular about how their code is structured and formatted, it is likely to take a little bit to get comfortable with LINQ syntax and having it sit in your code. For those whi have routinely heard the mantra not to embed SQL queries within your code, it will take a bit to get comfortable that you are not indeed doing anything wrong or dirty by using LINQ, but rather quite the opposite.
Even though LINQ absolutely is not limited just to accessing databases, I have found it of value in helping people to understand LINQ to first examine a SQL statement and then introducing the in-code LINQ representation of the same thing. The following SQL statement is one constructed against the Northwind sample database common to Microsoft SQL Server. The query is pretty basic and simply pulls a list of customers that are not located in the city of Berlin.
SELECT c.CompanyName, c.ContactName, c.CityFROM Customers cWHERE c.City != 'Berlin'ORDER BY c.ContactName
Now, look at a LINQ representation of the same thing and dissect it to understand the pieces and parts. There are two different types of query syntaxes: query expressions and method queries. You’ll focus on query expressions for now. The following query expression would search the IEnumerable type returned from GetCustomers() and find those that did not have a City location of Berlin. For this example, you’ll assume the GetCustomers method accesses the database and returns an IEnumerable type.
var customerNotInBerlin = from c in GetCustomers() where c.City != "Berlin" orderby c.ContactName select c;
The following table outlines some of the options available with LINQ syntax.
Destination | var <variable> = | Using type inference to assign the resulting value(s) |
Source | from <item> in <data source> | Information source providing a set of item(s) |
Filter | where <expression>, distinct | Expression specifying the selection criteria |
Order | order by <expression>, <expression> [Ascending | Descending] | Control the ordering of the results |
Aggregate | count([<expression>]), sum(<expression>), min(<expression>), max(<expression>), avg(<expression>) | Aggregate the source items |
Projection | select <expression> | Shaping the output |
There are many more options and variations of syntax than what is provided above, but this should give you a starting point for familiarity.
Test Driving LINQ Through Examples
Now that you’ve covered a little of the background, take LINQ for a test drive through a few handy examples. You’ll look at how to use LINQ to read data from a structured type, a file, and an example that involves using LINQ to access the EventLog.
Accessing a Structured Type
You’ll build on the example you were using earlier to show the syntax and query a structured type. You’ll create a Customer structured type and type in some data from the Northwind database to fill your Customer structured type. You’ll select all of the records where the City is not Berlin and then display them to the console. Because you’re selecting Customer objects, the ToString method you’ll add to your Customer will be used to display a comma-delimited list of attributes.
using System;using System.Collections.Generic;using System.Linq;namespace LINQIntro{ class Customer { public string CustomerName { get; set; } public string ContactName { get; set; } public string City { get; set; } public override string ToString() { return this.CustomerName + ", " + this.ContactName + ", " + this.City; } } class Program { static void Main(string[] args) { Program.ShowCustomers(); } public static void ShowCustomers() { // Build a list of customers using an object initializer List<Customer> customers = new List<Customer> { new Customer { CustomerName = "Alfreds Futterkiste", ContactName = "Maria Anders", City = "Berlin"}, new Customer { CustomerName = "Ana Trujillo Emparedados y helados", ContactName = "Ana Trujillo", City = "México D.F."}, new Customer { CustomerName = "Antonio Moreno Taquería", ContactName = "Antonio Moreno", City = "México D.F."}, new Customer { CustomerName = "Around the Horn", ContactName = "Thomas Hardy", City = "London"}, new Customer { CustomerName = "Berglunds snabbköp", ContactName = "Christina Berglund", City = "Luleå"}}; // Query the list of customers and select whatever // comes back var customer = from c in customers where c.City != "Berlin" orderby c.ContactName select c; // Display the selected records to the console foreach (var row in customer) { Console.WriteLine(row); } Console.ReadLine(); } }}
Showing a List of Files
In this example, you’ll use LINQ to search a directory on the local PC for files that start with a particular name. Once again, I chose the directory and filename at random based on items on my computer. You’ll want to adjust the directory and file in the query statement to ensure it will work for you.
using System;using System.Collections.Generic;using System.Linq;namespace LINQIntro{ class Program { static void Main(string[] args) { Program.ShowFiles(); } public static void ShowFiles() { // Point to a specific directory System.IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo( "C:Program FilesMicrosoft Visual Studio 9.0"); // Find files in the directory structure and select it var directoryList = from f in dirInfo.GetFiles("*.*", System.IO.SearchOption.AllDirectories) where f.Name.StartsWith("re") select f; // Display the selected records to the console foreach (var row in directoryList) { Console.WriteLine(row); } Console.ReadLine(); } }}
Showing the Running Processes
In this example, you’ll use LINQ to get the list of processes running on the local PC and display the list to the console.
using System;using System.Collections.Generic;using System.Linq;namespace LINQIntro{ class Program { static void Main(string[] args) { Program.ShowProcesses(); } public static void ShowProcesses() { // Select the list of processes and shape the output var processes = from p in System.Diagnostics.Process.GetProcesses() orderby p.ProcessName select new { p.ProcessName, p.Id, p.WorkingSet64, p.Threads, p.HandleCount }; // Display the selected records to the console foreach (var row in processes) { Console.WriteLine(row); } Console.ReadLine(); } }}
Accessing the Application Event Log
In this example, you’ll use LINQ to read data from the Application Event Log. You’ll search the EventLog for an error that contains a specific message. I chose the message to search for at random based on an error message I found in my local EventLog. You’ll want to adjust the query statement to find something in your local EventLog. For anyone who has done anything with the EventLog and isn’t yet a believer in LINQ, this example should give you a great example of the power and value of LINQ.
using System;using System.Collections.Generic;using System.Linq;namespace LINQIntro{ class Program { static void Main(string[] args) { Program.ShowEventLog(); } public static void ShowEventLog() { // Attach to the Application Event Log System.Diagnostics.EventLog myLog = new System.Diagnostics.EventLog(); myLog.Log = "Application"; // Query for an error record with "terminated" in the text // You'll want to adjust the EventLogEntryType and message // to find entries in your local event log // Notice how a custom object is being shaped var logEntries = from System.Diagnostics.EventLogEntry e in myLog.Entries where e.EntryType == System.Diagnostics.EventLogEntryType.Error && e.Message.Contains("terminated") select new { e.Source, e.InstanceId, e.Message, e.TimeGenerated }; // Display the selected records to the console foreach (var row in logEntries) { Console.WriteLine(row); } Console.ReadLine(); } }
Summary
You were given some introductory information about what LINQ is and LINQ is not. You’ve seen a brief background on the enabling language features. You’ve had an introduction to the LINQ syntax followed by a test drive of LINQ through a few examples. Hopefully, this gave you a good idea of the background behind LINQ and a better idea of the power that you can apply in your data access.
Future Columns
The topic of the next column is going to be either LINQ to XML or LINQ to SQL. If you have something else in particular that you would like to see explained here, you could reach me at mstrawmyer@crowechizek.com.
About the Author
Mark Strawmyer is a Senior Architect of .NET applications for large and mid-size organizations. Mark is a technology leader with Crowe Chizek in Indianapolis, Indiana. He specializes in the architecture, design, and development of Microsoft-based solutions. Mark was honored to be named a Microsoft MVP for application development with C# for the fifth year in a row. You can reach Mark at mstrawmyer@crowechizek.com.