LINQ and its linkage to new features in .NET Framework 3.5 – Part I,,,
Language Integrated Query (LINQ) related changes are going to be significant ones in .NET Framework 3.5 that is going to be released with next version of VS – code named Orcas. .NET Framework 3.5 enables the developers to use the managed languages (C# Version 3.0 and VB Version 9.0) to define the query on relational database, hierarchical infosets such as XML or .NET collection objects such as Arrays, in highly expressive manner. LINQ is meant to give the power to the mainstream development community in dealing with data on various sources, in consistent fashion within the domain of the managed languages using the familiar and flexible object oriented programming notations.
To get the taste of it, see the following code snippet, which queries the play’s titles that exceeds 14 characters in length.
using System.Linq;
using System.Collections.Generic;
using System.Text;
namespace MyConsoleApp
{
class Program
{
static void Main(string[] args)
{
string[] Names = { "Waiting for Godot",
"Death of a Salesman",
"Rosencrantz and Guildenstern Are Dead",
"The Devil's Disciple",
"Mrs. Warren's Profession",
"Arms and the Man",
"Candida",
"You Never Can Tell",
"Caesar and Cleopatra",
"Man and Superman",
"Major Barbara",
"The Doctor's Dilemma",
"Pygmalion",
"My Fair Lady",
"The Pretenders",
"Peer Gynt",
"Emperor and Galilean",
"A Doll's House",
"An Enemy of the People",
"The Wild Duck",
"Hedda Gabler",
"The Master Builder",
"Hamlet",
"Macbeth",
"Othello",
"Venus and Adonis",
"A Midsummer Night's Dream",
"As You Like It",
"Henry VI Part I",
"Henry V",
"Henry IV Part I",
"Henry VIII",
"Richard II",
"Richard III"
};
IEnumerable<string> name = from s in Names
orderby s.Length
where s.Length >= 15
select s;
foreach (string str in name)
Console.WriteLine("{0}", str);
Console.WriteLine("=======================");
Console.Read();
}
}
}
The output of the query is
The query expression that achieves this result is underlined in the following code snippet.
IEnumerable<string> name = from s in Names
orderby s.Length
where s.Length >= 15
select s;
LINQ provides T-SQL kind of outlook to the C# code. Though it exactly look like query statements, internally this piece of code will be converted into the following .NET notation format.
IEnumerable<string> name = Names.OrderBy(s => s.Length)
.Where(s => s.Length >= 15)
This query expression is made up of standard query operators like Select, Where and OrderBy. There are many such standard operators like SelectMany, Count, All, Any, Reverse, Distinct, Range, Repeat, etc are defined in System.Linq’s Enumerable class. These query operators are defined as the static methods in Enumerable class which is a static one. However, these methods are bit unique from being normal static methods. First of all, these methods can be invoked similar to instance method. And the first parameter to this static method mandatorily identifies the type to which it is attached. Basically, this static method is to extend certain logic of a specific type from the outside of that type. For example, all those above said standard query operators are extension methods over IEnumerable<T> object, but they are implemented as the static methods to the static class – Enumerable in System.Linq namespace. These extension methods are backbones for implementing highly expressive query syntaxes in managed languages.
The following code snippets show how these three - Select, Where and OrderBy -standard query operators are defined as Extension methods in Enumerable class.
Select query operator definition in C#
public static IEnumerable<TResult> Select<TSource,TResult> (
IEnumerable<TSource> source, Func<TSource,TResult> selector)
Select query operator definition in VB.NET
Public Shared Function Select(Of TSource, TResult) ( _
source As IEnumerable(Of TSource), _
selector As Func(Of TSource, TResult) ) As IEnumerable(Of TResult)
Where query operator definition in C#
public static IEnumerable<TSource> Where<TSource> (
IEnumerable<TSource> source,
Func<TSource,bool> predicate )
Where query operator definition in VB.NET
Public Shared Function Where(Of TSource)( _
ByVal source As IEnumerable(Of TSource), _
ByVal predicate As Func(Of TSource, Boolean) _
) As IEnumerable(Of TSource)
OrderBy query operator in C#
public static IOrderedSequence<TSource> OrderBy<TSource,TKey> (
IEnumerable<TSource> source,
Func<TSource,TKey> keySelector )
OrderBy query operator in VB.NET
Public Shared Function OrderBy(Of TSource, TKey)( _
ByVal source As IEnumerable(Of TSource), _
ByVal keySelector As Func(Of TSource, TKey) ) As IOrderedSequence(Of TSource)
Let us observe the extension method definition of “Select”.The first parameter “IEnumerable<TSource> source” lets this method to attach itself to “IEnumerable<T>” type. It also means that this method can be invoked on the instance of the type “IEnumerable<TSource>“. The second parameter is nothing but a delegate type and its equivalent delegate syntax is as given below.
public delegate TResult Func<TArg0,TResult> (
)
[Delegate definition in VB.NET is
Dim instance As New Func(Of TArg0, TResult)(AddressOf HandlerMethod)]
To understand the underlying concepts clearer, let us create an extension method for the standard query operator – Select in one of our custom static class – ShakespeareWorks. Then let us use our custom extension method (or our custom standard query operator) to override the standard query operator that is defined on System.Linq’s Enumerable class.
In the custom standard query operator, I implemented the logic so that it will select only the works of Shakespeare. The following code snippet provides the definition of the extension method. The keyword “this” in the underlined portion of the code is mandatory as it allows the instance of type IEnumerable<T> to invoke this method.
using System;using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Collections;
namespace System.ShakespeareQuery
{
public static class ShakespeareWorks
{
public static IEnumerable<T> Select<T>(this IEnumerable<T> source, Func<T, string> selector)
{
string[] dramasList = {
"Comedy of Errors",
"Titus Andronicus",
"Pericles",
"Cymbeline",
"All's Well That Ends Well",
"Taming of the Shrew",
"Love's Labour's Lost",
"Merchant of Venice",
"Romeo and Juliet",
"Henry IV part I",
"Henry IV part II",
"Henry V",
"Richard II",
"Richard III",
"Henry VI Part I",
"Henry VI Part II",
"Henry VI Part III",
"Henry VIII",
"Much Ado About Nothing",
"As You Like It",
"Julius Caesar",
"Hamlet",
"King John",
"Othello",
"Twelfth Night",
"The Two Gentleman of Verona",
"Macbeth",
"King Lear",
"The Winter's Tale",
"A Midsummer Night's Dream",
"The Merchant of Venice",
"Merry Wives of Windsor",
"Troilus and Cressida",
"Measure for Measure",
"Coriolanus",
"Antony and Cleopatra",
"Timon of Athens",
"The Tempest",
"The Two Noble Kinsmen"
};
foreach (T item in source)
{
foreach (string str in dramasList)
{
if (string.Compare(selector(item).ToLower(), str.ToLower()) == 0)
yield return item;
}
}
}
}
In order to use our custom query operator, we have to refer the namespace “System.ShakespeareQuery” (by importing that through “using”) in our console application wherein we are going to write query on the play’s titles.
using System;using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.ShakespeareQuery;
namespace MyConsoleApp
{
class Program
{
static void Main(string[] args)
{
string[] Names = { "Waiting for Godot",
"Death of a Salesman",
"Rosencrantz and Guildenstern Are Dead",
"The Devil's Disciple",
"Mrs. Warren's Profession",
"Arms and the Man",
"Candida",
"You Never Can Tell",
"Caesar and Cleopatra",
"Man and Superman",
"Major Barbara",
"The Doctor's Dilemma",
"Pygmalion",
"My Fair Lady",
"The Pretenders",
"Peer Gynt",
"Emperor and Galilean",
"A Doll's House",
"An Enemy of the People",
"The Wild Duck",
"Hedda Gabler",
"The Master Builder",
"Hamlet",
"Macbeth",
"Othello",
"Venus and Adonis",
"A Midsummer Night's Dream",
"As You Like It",
"Henry VI Part I",
"Henry V",
"Henry IV Part I",
"Henry VIII",
"Richard II",
"Richard III"
};
IEnumerable<string> name = Names.OrderBy(s => s.Length)
.Where(s => s.Length >= 15)
.Select(s => s);
foreach (string str in name)
Console.WriteLine("{0}", str);
Console.WriteLine("=======================");
Console.Read();
}
}
}
When we run the application, the standard query operator of the Enumerable class will be overridden with our custom standard query operator as we have referenced our custom namespace that contains the static class wherein we have implemented our extension method, for standard query operator. So, the result will be the display of only Shakespeare’s works whose title exceeds 14 characters, on the screen.
However, readers would have noticed strange expressions like s => s.Length; s => s.Length >= 15; s => s that are passed as arguments to the standard query operators. These expressions are known as Lambda expressions and their main aim is to succinctly represent the code of the function that is passed to the query operators in expression format. In other terms, they are nothing but the short and concise representation of the anonymous method’s logic. In fact, we can expand the lambda expression into verbose delegate format as given below.
Func<string, string> selector = delegate(string arg0){
return arg0;
};
IEnumerable<string> name = Names.OrderBy(s => s.Length)
.Where(s => s.Length >= 15)
.Select(selector);
The above code snippet can also be written as,
IEnumerable<string> name = Names.OrderBy(s => s.Length)
.Where(s => s.Length >= 15)
.Select(delegate(string arg0) { return arg0; });
Apart from Extension method, Lambda expression, there is a handful of new features such as Object initializer, Anonymous type and Implicit local type inference in .NET Framework 3.5.
In the following code snippet, the variable “drama” has been declared with newly introduced syntax “var” and newed up with a property value set. When we new up, you might notice that I do not mention the name of the type. What I do here is I create an anonymous type, a class with three public properties of “string” type – Name, Author and Synopsis.
IEnumerable<string> name = Names.OrderBy(s => s.Length)
.Where(s => s.Length >= 15)
.Select(s => s);
var drama = new { Name = "", Author = "", Synopsis = "" };
foreach (string str in name){
drama = new { Name = str, Author = "Shakespeare", Synopsis = ""
};
Console.WriteLine("{0} Written By {1}", drama.Name, drama.Author);
The output of the above code snippet is
So, various language constructs have been created and strung together to make the primary .NET languages to have the expressive power of query languages. However, these language constructs can be used outside of LINQ context too. In future sequels to this blog, let us see some interesting details on query expression and LINQ support to relational data and hierarchical XML.
