Browse DevX
Sign up for e-mail newsletters from DevX


LINQ Into Microsoft's New Query Capabilities : Page 2

Query features have long been a cornerstone of database applications, but with LINQ, Microsoft introduces query language features right inside of C# and VB.NET.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

A More Useful Example
My example so far was perfectly functional, but it was also completely useless because the result set is identical to the source version. This example makes more sense.

IEnumerable<string> result = from name in names orderby name select name;

In this case my result set is ordered by the name. I can print these names in the following fashion:

foreach (string s in result) { Console.WriteLine(s); }

This prints the following result to the Output window:

Ellen Erna Franz Markus

I can, of course, also add a WHERE clause to my query.

IEnumerable<string> result = from name in names orderby name where name.StartsWith("E") select name;

And my result looks like this.

Ellen Erna

LINQ will bring practically all standard query operators (GROUP BY, SUM, JOIN, UNION, etc.) to the core C# and VB.NET languages.

Author's Note: The C# flavor is LINQ is currently documented in a much more complete fashion. For this reason, I am using mostly C# examples. Nevertheless, VB.NET supports LINQ just as well as C# does. Some would even argue that VB.NET supports LINQ better than C#.

The Magic of Objects
Everything in .NET is objects, and therefore, LINQ is based exclusively on objects. This little fact turns the LINQ query language into something that is a lot more powerful than a query language that just deals with data. To understand why, I must show you a few examples.

Listing 1 shows a Customer class, which I will use for some examples, as well as a helper method that instantiates a list of customers. Once you have this list of customer objects in memory, I can query from that list like so:

List<Customer> customers = Helper.GetCustomerList(); IEnumerable<Customer> result = from c in customers orderby c.CompanyName where c.ContactName.StartsWith("A") select c;

This returns a list of customers where the contact person's name starts with an "A." My example also sorts the result by company name. Note that the result set is an enumerable list of Customer objects, since I selected "c", and "c" is the name I assigned to each customer in the list. Of course, the result could have also been something entirely different, such as a single property of that Customer object.

IEnumerable<string> result = from c in customers orderby c.CompanyName where c.ContactName.StartsWith("A") select c.Country;

In this example, the result is a list of country names (strings) for the same customers.

Note that not just the SELECT clause changed, but the declaration of the result type as well. In the previous example I used IEnumerable<Customer>, while the current example results in IEnumerable<string>. The result type is dictated by the SELECT part of the command and can not be altered in any other way. Therefore, one could argue that it is redundant and developers should not have to declare the type of the result variable. As it turns out, Anders Hejlsberg (the "father" of C#) agrees with that viewpoint and has added a new feature to C# 3.0 known as "type inference." Using this feature, I could also define the last example in the following fashion:

var result = from c in customers orderby c.CompanyName where c.ContactName.StartsWith("A") select c.Country;

The declaration of "result" as "var" simply indicates to the compiler that it is to infer the real type based on the expression. The compiler can analyze the SELECT statement and therefore figure out that "var" really needs to be "IEnumerable<string>" (in this example). Don't confuse "var" with "variant" in which scripting languages use. Instead, "var" is still a strongly typed statement. You just leave it up to the compiler to figure out what the type should be.

You can also use type inference in other instances. Look at these perfectly fine and strongly typed C# 3.0 statements:

var name = "Markus"; var frm = new System.Windows.Forms.Form();

But, I digress. There still are many unexplored things you can do with objects as data sources. In the examples so far, I've shown you how to perform simple queries that use features available on .NET standard types such as strings. Selecting names starting with "A" is the equivalent of the following SQL Server statement:

SELECT * FROM Customers WHERE ContactName like 'A%'

SQL Server knows a number of standard types (such as strings) and can thus apply certain operations, such as an "=" or "like" operator. In LINQ, on the other hand, data sources could be any type of objects, and the features and abilities of those objects are only limited by your imagination. The Customer class I've used in my examples has such custom functionality. Here is another example. You could use the IsOddCustomer() method, which tells you whether or not the customer number is odd (or even). You could use this method in LINQ queries:

var result = from cust in customers where cust.IsOddCustomer() select cust;

This has significant implications since it means that you have complete control over the behavior of the WHERE clause (or any other part of the statement for that matter). For instance, it is possible to include a significant amount of business logic in the code called by the WHERE clause, which would not be feasible in the same way in SQL Server. For instance, the method called could in turn call Web services or invoke other objects. (Note that it is fine to do this in terms of architecture, because this code is likely to run in the middle tier).

Another aspect of using objects instead of data in a query language is that the result set can be any type of objects. In the following example, I'll assume an array of objects of type ShortCustomer. This is a list of Customer objects where each object has two properties: Country and PrimaryKey. I can use a LINQ query to SELECT all primary keys for customers from a certain country, but instead of returning that primary key directly, I can use it to instantiate new Customer objects.

var result = from c in customerList where c.Country == "USA" select new Customer(c.PrimaryKey);

This means that for each selected primary key, the code must instantiate a new Customer object. (Presumably, the Customer class loads the complete customer information into the object when launched this way, but this is completely up to that class). The result of this query is a list of Customer objects. This is interesting, because in essence, the result set is a list of objects that was in no way contained in the original query source in any way other than the objects being identified by their primary key.

Now I'll spin this example a bit further as well and do this:

var result = from c in customerList where c.Country == "USA" select new CustomerEditForm(c.PrimaryKey);

This returns a list of edit forms for each customer from the US. The only problem at this point is that those forms are not displayed yet, so we still need to make them visible.

foreach (Form frm in result) frm.Show();

Of course, this query may end up opening a very large number of windows, so you probably don't want to use it in real-life applications. However, this example demonstrates how you could apply the LINQ query language to anything .NET has to offer and not just data. Of course, this also works the other way around. For instance, you could query all controls on a form that have certain content (or no content, or…) and then join the result with data from a different data source and union it together with… well, you get the idea.

Thanks for your registration, follow us on our social networks to keep up-to-date