Browse DevX
Sign up for e-mail newsletters from DevX


LINQ Into Microsoft's New Query Capabilities : Page 3

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

Anonymous Types and Object Initialization
When you run a query, you often expect a result set that contains a limited selection of information contained in the data source. Consider this SQL Server example:

SELECT CompanyName, ContactName FROM Customer

This returns two fields from the much larger list of fields in the Customer table. Of course, you might want to do this in C# and VB.NET. However, since every result must be an object (or a list of objects), this requires some object type with exactly these two properties. Chances are that you don't have such a class, and creating such a class for each query result would be cumbersome and seriously take away from the power of the query language. Therefore, new features are needed and C# 3.0 will offer them! Two in particular are important for this scenario: object initialization and anonymous types.

Object initialization deals (surprise!) with the initialization of public members (properties and fields). Consider this conventional example:

Customer cust = new Customer(); cust.CompanyName = "EPS Software Corp."; cust.ContactName = "Markus Egger";

Using object initialization I could also write this example in a single source line (single statement):

Customer cust = new Customer() { CompanyName = "EPS Software Corp.", ContactName = "Markus Egger"};

Author's Note: Due to column width constraints in the magazine, this statement ends up as three lines, but it is only a single line of source code as far as the compiler is concerned.

This feature is particularly useful in LINQ queries:

var result = from n in names select new Customer() {ContactName = n.FirstName + " " + n.FirstName};

The second important C# 3.0 feature, anonymous types, allows you to create a new object type simply based on necessity and requirements derived from the type's usage. Here's an example:

new {CompanyName = "EPS Software Corp.", ContactName = "Markus Egger"};

While similar to my previous example, the new operator does not specify the name of the class that is to be instantiated. In fact, you don't need to instantiate a defined class. Instead, the compiler realizes that you need a type with two properties based on the fact that the code attempts to initialize them. Therefore, the compiler creates a class behind the scenes that has the required properties (and fields) and uses it on the spot. Note that the only way to use such a type is through type inference (see above).

var customer = new { CompanyName = "EPS Software Corp.", ContactName = "Markus Egger"};

You can use object initialization and anonymous types features in queries. For instance, you can query from a list of Customer objects (Listing 1) and return brand new objects with two properties.

var result = from cust in customers select new { Name = cust.CompanyName, Contact = cust.ContactName};

Note that this example also would not be possible without type inference since there would be no way to define the type of the "result" variable, since the name of that type is unknown.

Object Syntax
Purists may have noted that the LINQ syntax is similar to T-SQL, but it is not entirely C#-like. In other words: almost everything else in C# is expressed as objects, while LINQ introduces the longest C# command sequence ever. As it turns out, the SELECT syntax is only window dressing. Behind the scenes, the compiler actually turns every SELECT statement into pure object syntax. Consider this example:

var result = from c in customers where c.ContactName == "Egger" select c.Country;

You could also write this statement like so:

var result = customers.Where( c => c.ContactName == "Egger" ) .Select( c => c.Country );

This will be normal C# 3.0 syntax. However, not a lot of people are using C# 3.0 yet so I need to explain a few details. I've already discussed the new "var" keyword used by type inference (see above). I need to explain a new feature of C# 3.0 called lambda expression that you see as the passed parameter. Lambda expressions are an evolution of C# 2.0's anonymous methods. Using lambda expressions you can pass code instead of data as method parameters. Basically, the Where() and Select() methods accept a delegate as their parameters, and the lambda expression provides the code for the delegate to execute.

The expression itself appears a bit unusual at first but is easy to understand. It starts out with input parameters ("c" in this case) followed by the "=>" operator, followed by the return value (or alternatively, a complete method body). You could also express the lambda expression c => c.ContactName == "Egger" as a complete method.

public var MyMethod(var c) { return (c.ContactName == "Egger"); }

Note that this example uses type inference in the lambda expression to determine the parameter as well as the return type. You could also explicitly type parameters for lambda expressions.

(Customer c) => c.ContactName == "Egger"

Lambda expressions are very powerful and can do everything delegates and anonymous methods can do, plus a few extra tricks I will show you below. Unfortunately, a complete exploration of features provided by lambda expressions is beyond the scope of this article.

The remaining mystery is the puzzling appearance of the Where() and Select() methods. For the object-syntax version to work, every object in .NET would have to have these methods. And in fact, with LINQ, they do! The reason is a mechanism that is also new in C# 3.0 called extension methods. These are special static methods defined on a static class. Whenever such as class is in scope (either because it is in the current namespace or by way of a using statement), then the extension method gets added to all objects that are currently in scope who do not already have a method of identical signature.

You should only use this somewhat radical feature when you really need to. However, in some scenarios, it is extremely useful. As an example, consider the string type and the possible need to add new methods to that class. In many scenarios you can do this through subclassing, but if you want to add a method to all strings, you cannot do that with subclassing. Using extension methods, this isn't technically possible either, but through a little compiler magic, you can at least create the illusion of an added method. Consider the following example which shows a ToXml() extension method:

public static class EM { public static string ToXml( this object extendedObject) { return "<value>" + extendedObject.ToString() + "</value>"; } }

Note that this is a method that is only different from standard static methods in that it uses the "this" modifier with the first parameter. The "this" modifier indicates that the first parameter is a reference to the object that is extended (extension methods must always have at least one parameter, which is a reference to the object it extends).

With your extension method created you can use it on all objects as long as the "EM" class is in scope (either because it is in the current namespace, or because it is in scope due to a USING statement). Therefore, the following statement is now valid:

string name = "Markus"; string xmlName = name.ToXml();

Note that the parameter does not appear in this version. Instead, the parameter is the object the method is seemingly used on. Behind the scenes, the compiler changes this example to standard object notation.

string name = "Markus"; string xmlName = EM.ToXml(name);

Extension methods create the illusion of added methods, and LINQ uses this ability extensively to add methods such as Select(), Where(), and Join(). Note that you can only add extension methods to objects that do not already have methods of identical name and signature.

Extension methods have a number of side effects that turn out to be quite useful. For one, developers can use individual pieces of functionality LINQ provides, without having to use other, possibly unwanted LINQ functionality. For instance, this example takes the contents of an array and returns them grouped by string length; a feature that is not available on arrays without LINQ.

string[] names = {"Markus", "Ellen", "Franz", "Erna" }; var result = names.GroupBy(s => s.Length);

Considering the different features available through LINQ (sorting, grouping, calculations, joins, unions, etc.), this certainly is a rather interesting side effect.

Another side effect of extension methods is that extension methods are only added on objects that do not already have a method of that name and signature. This means that developers can purposely implement such methods to explicitly replace how certain features of LINQ work. For instance, if you do not like how LINQ calculates averages using the Average() function (or "average" keyword), then you can implement your own Average() method which you can then use on your object. (Standard LINQ functionality keeps being used on all other objects).

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