C# 3.0 incorporates LINQ (Language INtegrated Query), a cool technology that adds dynamic queries to all kinds of objects in a familiar SQL-like style. LINQ code looks like embedded SQL statements, which I actually don’t care for because SQL has never looked pretty to me. I also do not believe that LINQ adds clarity to code. It looks terse and somewhat ambiguous. In a nutshell, LINQ is a sexy technology (as they are fond of saying in Redmond), but it is not pretty.
This article shows you how to get LINQ for Visual Basic and begin using it now. As you read, you can begin formulating your own opinion of its elegance and usefulness.
Using LINQ for VB Now
Begin by downloading and installing LINQ. This article uses the May 2006 CTP (Community Technology Preview).
[Tip: If you like bleeding-edge technologies and want to know what Microsoft is working on for future products, check out research.microsoft.com. LINQ, for example, came out of the Omega research project.]
As with all pre-release software, you download and install with some risk. In this case, LINQ will cause minor problems with IntelliSense and CodeRush. If you rely on CodeRush, Refactor, and IntelliSense, then install LINQ on a Virtual PC instance with Visual Studio 2005 on it.
Working with the LINQ CTP
Once you have downloaded and installed LINQ, you will notice a new folder named C:Program FilesLINQ Preview. This folder contains LINQ documentation, assemblies, and examples. When you run Visual Studio after installing LINQ, you will get a message indicating LINQ for C# is installed, but LINQ for VB also will be installed.
To use LINQ and create a project, pick one of the LINQ project templates as shown in Figure 1. These project templates will add the appropriate references to LINQ assemblies and any necessary import statements.
Figure 1: LINQ Project Templates
All of the samples in this article are part of a console application, and the referenced assemblies—including Microsoft.VisualBasic.LINQ, System.Data.DLinq, and System.Xml.XLinq—will be added to your project’s list of references (see Figure 2). In fact, LINQ is supported for ADO and XML too.
Figure 2: References to Essential LINQ Assemblies
[Tip: To view the references in a VB project, click the Show all files button (highlighted in blue in Figure 2) in the Solution Explorer.]
Having installed LINQ, let’s look at some of the things you can do with this technology.
Using LINQ
LINQ is a query language for .NET—VB in the examples to follows. To be useful and intuitive, I would expect LINQ to do almost everything SQL can do. Although I didn’t test its limits, LINQ proved pretty intuitive as I developed the examples. I literally guessed at them based on what I anticipated from a query language. The following sections discuss some of the queries you can write with LINQ.
Selecting Numbers from an Array
LINQ for VB looks a little like reverse polish notation for SQL. For example, you can define an array of integers and then query that array using a WHERE predicate to return a subset of integers from an unordered set. Listing 1 shows a VB subroutine that does just that with a subset of fewer than 10 integers.
Listing 1: Select Numbers from an Array of Unordered Integers Using LINQ
Sub SampleOne()
Dim numbers = {2, 1, 34, 5, 8, 13, 21, 33}Dim lessThanTen = From num In numbers _
Where num < 10 _
Select numFor Each Dim n In lessThanTen
Console.WriteLine(n)
NextConsole.ReadLine()
End Sub
This exampe defines an array of random integers. The LINQ query (shown in bold) defines an anonymous type lessThanTen, which becomes an instance of IEnumerable. (Anonymous types are much more spectacular to C# programmers; you VB programmers are used to this type of notation.) The LINQ query From num in numbers Where num < 10 Select Num initializes lessThanTen. And, of course, most of us know by now that any IEnumerable type can be used in a For Each statement. The result from Listing 1 are the numbers 1, 2, 5, and 8 printed to the console.
Notice that numbers uses an array initializer without explicitly declaring numbers as an array (using numbers()). The variable numbers‘s type is an anonymous type whose actual type is inferred. The same is true of the anonymous variable n. (Note the unusual location of the keyword Dim in the For Each statement. This is valid .NET 3.0 code.)
Using Ordering Clauses in LINQ
Listing 2 defines an unordered array of Fibonacci numbers and uses an ORDER clause to order the array. (Google Fibonacci for a definition. You will find these numbers pretty interesting.)
Listing 2: Ordering an Array of Integers Using LINQ
Sub SampleTwo()
Dim numbers = {2, 1, 34, 5, 8, 13, 21, 3}Dim ordered = From num In numbers _
Select num _
Order By numFor Each Dim n In ordered
Console.WriteLine(n)
NextConsole.ReadLine()
End Sub
Listing 2 is like Listing 1 but with the addition of the Order By clause. The anonymous type ordered contains the re-ordered list of integers: 1, 2, 3, 5, 8, 13, 21, 34. Adding the keyword Descending after the clause Order By num will sort the list in reverse order. This variant is shown in Listing 3.
Listing 3: Sorting in Reverse Order
Sub SampleThree()
Dim numbers = {2, 1, 34, 5, 8, 13, 21, 33}Dim ordered = From num In numbers _
Select num _
Order By num DescendingFor Each Dim n In ordered
Console.WriteLine(n)
NextConsole.ReadLine()
End Sub
Sorting Generic Lists of Custom Types
You can also apply LINQ to custom types. Suppose I define a custom type Customer (see Listing 4) and want to sort a generic list of customers.
Listing 4: A User-Defined Customer Class
Public Class CustomerPrivate fid As Integer
Private fname As String
Private fcity As StringPublic Sub New(ByVal id As Integer, ByVal name As String, _
ByVal city As String)
fid = id
fname = name
fcity = city
End SubPublic Overrides Function toString() As String
Dim mask As String = “{0} is in {1}”
Return String.Format(mask, fname, fcity)
End FunctionPublic Property ID() As Integer
Get
Return fid
End Get
Set(ByVal value As Integer)
fid = value
End Set
End PropertyPublic Property Name() As String
Get
Return fname
End Get
Set(ByVal value As String)
fname = value
End Set
End PropertyPublic Property city() As String
Get
Return fcity
End Get
Set(ByVal value As String)
fcity = value
End Set
End PropertyEnd Class
I can define the Customer class and instantiate a List(Of T) where T is a customer using a specific field from the class Customer (see Listing 5).
Listing 5: Initializing a Generic List of Customer Objects and Sorting Based on the City Field
Sub SampleFour()Dim customers = New List(Of Customer)
customers.Add(New Customer(1, “Kimmel Computers”, “Plattsburgh”))
customers.Add(New Customer(2, “Fred’s Frieds”, “Memphis”))
customers.Add(New Customer(3, “House of Clams”, “Detroit”))Dim ordered = From cust In customers _
Select cust _
Order By cust.CityFor Each Dim c In ordered
Console.WriteLine(c.toString())
NextConsole.ReadLine()
End Sub
Implementing IComparable
If you modify Customer by implementing Icomparable, you can tell Customer objects how to compare themselves. Implement IComparable by adding the Implements IComparable statement to the Custom class and adding the Function CompareTo. Listing 6 shows the revised Customer class, and Listing 7 shows the revised code from Listing 5 that now uses LINQ to sort based on the implementation of IComparable.
Listing 6: Implement IComparable to Compare Customer Objects by City
Public Class Customer
Implements IComparablePrivate fid As Integer
Private fname As String
Private fcity As StringPublic Sub New(ByVal id As Integer, ByVal name As String, _
ByVal city As String)
fid = id
fname = name
fcity = city
End SubPublic Overrides Function toString() As String
Dim mask As String = “{0} is in {1}”
Return String.Format(mask, fname, fcity)
End FunctionPublic Property ID() As Integer
Get
Return fid
End Get
Set(ByVal value As Integer)
fid = value
End Set
End PropertyPublic Property Name() As String
Get
Return fname
End Get
Set(ByVal value As String)
fname = value
End Set
End PropertyPublic Property city() As String
Get
Return fcity
End Get
Set(ByVal value As String)
fcity = value
End Set
End PropertyPublic Function CompareTo(ByVal obj As Object) _
As Integer Implements System.IComparable.CompareTo
Return city.CompareTo(obj.City)
End Function
End Class
Listing 7: Sorting a Generic List of IComparable Customer Objects
Sub SampleFive()
Dim customers = New List(Of Customer)
customers.Add(New Customer(1, “Kimmel Computers”, “Plattsburgh”))
customers.Add(New Customer(2, “Fred’s Frieds”, “Memphis”))
customers.Add(New Customer(3, “House of Clams”, “Detroit”))Dim ordered = From cust In customers _
Select cust _
Order By custFor Each Dim c In ordered
Console.WriteLine(c.toString())
NextConsole.ReadLine()
End Sub
The only difference between Listing 7 and Listing 5 is that the Order By clause now specifies that you are sorting only Customer objects. The sort field is implicit as defined by the CompareTo function.
A SQL-Like Query Language
This article introduced LINQ and demonstrated how you can combine this technology with existing technologies such as interfaces and generic lists to support a SQL-like query language in your VB.NET code. Of course, LINQ (and .NET 3.5) introduce many more capabilities, including anonymous types and support for ADO.NET and XML. Go ahead and explore.
About the Author
Paul Kimmel is the VB Today columnist for www.codeguru.com and has written several books on object-oriented programming, including Visual Basic .NET Power Coding (Addison-Wesley) and UML Demystified (McGraw-Hill/Osborne). He is the president and co-founder of the Greater Lansing Area Users Group for .NET (www.glugnet.org) and a Microsoft Visual Developer MVP.
Copyright © 2006 by Paul T. Kimmel. All Rights Reserved.