|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionIn this article, I will cover the following topics:
Differed and Immediate ExecutionBy default, LINQ query expressions are not evaluated until you iterate over the contents. The benefit of this differed execution allows the same LINQ query to be executed multiple times, giving you the latest results. However, there would be times when differed execution behavior would not be acceptable due to performance reasons of the query being executed every time. To prevent this behavior, the .NET Framework defines a number of extension methods such as public static void DifferedAndImmediateExecution()
{
int[] numbers = { 1, 2, 3, 4, 5, 6 };
var lessthan4 = from n in numbers
where n < 4
select n;
Console.WriteLine("Original array with items less than 4");
foreach (int n in lessthan4)
{
Console.WriteLine("{0} < 4", n);
}
//differed execution
numbers[2] = 7;//assigning new value
Console.WriteLine(
"Results based on differed execution after change number[2] = 7");
foreach (int n in lessthan4)
{
Console.WriteLine("{0} < 4", n);
}
//immediate execution
numbers = new int[] { 1, 2, 3, 4, 5, 6 };
var lessthan4immediate = numbers.Where(n => n < 4).Select(n => n).ToArray<int>();
numbers[2] = 7;//assigning new value
Console.WriteLine(
"Results based on immediate execution after change number[2] = 7");
foreach (int n in lessthan4immediate)
{
Console.WriteLine("{0} < 4", n);
}
}
Figure 1
As you should notice, new changes are reflected on differed queries whereas new changes are not reflected on immediate execution because the results are cached in a strongly typed container. It may appear in the example that the query is happening when public static void DelayedExceptions()
{
string[] blob = {"Resharper", "is", "great"};
IEnumerable<string> sentence = blob.Select(s => s.Substring(0, 5));
foreach (string word in sentence)
{
Console.WriteLine(word);
}
}
You will notice that when you loop the second time is when you get an exception of
Support for Non-generic Collections.
Most of the extension methods work with public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
public class Car
{
public string Make { get; set; }
public string Model { get; set; }
}
public static void OfTypeExtesionMethod()
{
ArrayList books = new ArrayList()
{
new Book{Title="ASP.NET 2.0 Website Programming: Problem -
Design - Solution",
Author="Marco Bellinaso"},
new Book{Title="Pro ASP.NET 3.5 in C# 2008, Second Edition",
Author="MacDonald and Mario Szpuszta"},
new Book{Title="ASP.NET 2.0 Unleashed", Author="Stephen Walther"},
new Book{Title="ASP.NET 3.5 Unleashed ", Author="Stephen Walther"},
new Car{Make="Toyota", Model="Corolla"}
};
IEnumerable<Book> bystephen = from book in books.OfType<Book>()
where book.Author == "Stephen Walther"
select book;
foreach (Book book in bystephen)
{
Console.WriteLine("Book {0} by {1}", book.Title, book.Author);
}
}
Figure 2
public static void CastOperator()
{
ArrayList list = new ArrayList();
list.Add(1);
list.Add(2);
list.Add("ASP.NET 3.5 Unleashed");
IEnumerable<int> numbers = list.Cast<int>();
foreach (int number in numbers)
{
Console.WriteLine(number);
}
}
When you run the cast operator, you get an exception because the cast operator tries to cast every element in the collection to the specified type. If it does not cast, it will throw an exception. In contrast, the Query SyntaxThe .NET Framework version 3.5 allows querying data using LINQ in a variety of different ways:
public static void QuerySyntax()
{
int[] numbers = { 1, 2, 3, 4, 5, 6 };
//using query syntax
var usingquerysntax = from n in numbers
where n < 5
select n;
//Extension Methods and Lamda expressions
var usingextensionmethods = numbers.Where(n => n < 5).Select(n => n);
//using IEnumerable<T>
Enumerable.Where(numbers, n => n < 5).Select(n => n);
//using generic delegate
Func<int, bool> where = n => n < 5;
Func<int, int> select = n => n;
var usinggenericdelegate = numbers.Where(where).Select(select);
//using expressions
Expression<Func<int, bool>> where1 = n => n < 5;
Expression<Func<int, int>> select1 = n => n;
var usingexpression = numbers.Where(where).Select(select);
//anonymous methods
var usinganonymousmethods = numbers.Where(delegate(int n) { return n < 5; })
.Select(delegate(int n) { return n; });
}
The above code would give the same result. Key things to consider are that query syntax is simply a nicer way to execute extension methods. Wherever a delegate is required, lambdas, generic delegates or an anonymous method can be passed. Expression TreeAn expression tree is an efficient data representation of a query's lambda expression. In terms of LINQ to SQL, the expression tree gets converted to SQL to ensure that filtering and ordering is performed on the server instead of bringing the data down and applying filtering and ordering on .NET objects. For lambda expressions, the compiler can generate IL code or an expression tree. If the operator accepts a method delegate, IL code is emitted. If an operator accepts an expression of a method delegate, the expression tree is returned. Let's look at the two different implementations of public static IEnumerable<TResult> Select<TSource,
TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector);
public static IQueryable<TResult> Select<TSource,
TResult>(this IQueryable<TSource> source,
Expression<Func<TSource, TResult>> selector);
As you can see, LINQ to objects generate IL code because it takes in a generic delegate, whereas a LINQ to SQL implementation takes an expression tree that gets converted to SQL to be executed by SQL server. Partial MethodsBy defining a method
public partial class PartialTest
{
partial void Partial(int total);
public void TestPartial()
{
int total = 0;
Console.WriteLine("Total before calling Partial method {0}", total);
Partial(++total);
Console.WriteLine("Total after calling Partial method {0}", total);
}
}
PartialTest test = new PartialTest();
test.TestPartial();
Figure 3
As you can see, there was no implementation defined for Returning Projections From MethodsYou may have noticed that the above code made use of an implicit type variable by using public static Array ReturnProjections()
{
Book[] books =
{
new Book{Title="ASP.NET 2.0 Unleashed", Author="Stephen Walther"},
new Book{Title="ASP.NET 3.5 Unleashed ", Author="Stephen Walther"}
};
var projections = from book in books
select new { book.Title };
return projections.ToArray();
}
Notice that we are not specifying the underlying type in the return type for Array books = ReturnProjections();
foreach (object o in books)
{
Console.WriteLine(o);
}
LINQ Over DatasetOut-of-the-box, datasets, datatables and dataviews do not have the necessary infrastructure to work in a LINQ query. This is where Microsoft introduced the new extension called System.Data.DataSetExtensions.dll, which includes public static void DataSetExample(DataTable books)
{
var implicittype = from book in books.AsEnumerable()
where book.Field<string>("Author") == "Stephen Walther"
select book;
var datatable = implicittype.CopyToDataTable();
}
Anonymous TypesThe C# 3.0 language includes the ability to dynamically create new unnamed classes. This type of class is known as an anonymous type. An anonymous type has no name and is generated by the compiler based on the initialization of the object being instantiated. Since the class has no type name, any variable assigned to an object of an anonymous type must have some way to declare it. This is the purpose of the new C# 3.0 public override string ToString()
{
StringBuilder builder = new StringBuilder();
builder.Append("{ BookTitle = ");
builder.Append(this.<BookTitle>i__Field);
builder.Append(", BookAuthor = ");
builder.Append(this.<BookAuthor>i__Field);
builder.Append(" }");
return builder.ToString();
}
public static void AnonymousTypeSemantics()
{
var book1 = new { BookTitle = "ASP.NET 3.5 Unleashed",
BookAuthor = "Stephen Walther" };
var book2 = new { BookTitle = "ASP.NET 3.5 Unleashed",
BookAuthor = "Stephen Walther" };
//check for ==
Console.WriteLine("Checking if book1 == book2");
if (book1 == book2)
{
Console.WriteLine("Same object");
}
else
{
Console.WriteLine("Not same object");
}
//check for equals
Console.WriteLine("Checking if book1.Equals(book2)");
if (book1.Equals(book2))
{
Console.WriteLine("Same object");
}
else
{
Console.WriteLine("Not same object");
}
}
The result is:
Figure 4
Standard Query OperatorsThe list of standard query operators is huge. I will cover a few of them that I found interesting and that require a bit of understanding. Select Many and Concat OperatorsThe public static IEnumerable<S> SelectMany<T, S>(
this IEnumerable<T> source,
Func<T, IEnumerable<S>> selector);
The operator takes an input sequence of public static void SelectManyOperator()
{
string[] items = { "this", "is", "a", "test" };
IEnumerable<char> characters = items.SelectMany(s => s.ToCharArray());
foreach (char character in characters)
{
Console.WriteLine(character);
}
}
Figure 5
As you can see from the result, for every single input of word, the select many operator yields an array of characters. The select many operator concatenates each of those arrays of characters into a single sequence. The public static void ConcatAndSelectManyOperator()
{
string[] books = {
"ASP.NET 2.0 Website Programming: Problem -
Design - Solution",
"Pro ASP.NET 3.5 in C# 2008, Second Edition",
"ASP.NET 2.0 Unleashed",
"ASP.NET 3.5 Unleashed "
};
IEnumerable<string> concatbooks = books.Take(2).Concat(books.Skip(2));
IEnumerable<string> selectmanybooks = new[]{
books.Take(2),
books.Skip(2)
}.SelectMany(b => b);
}
Using OrderBy OperatorsThe public static IOrderedEnumerable<T> OrderBy<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector)
where
K : IComparable<K>
The operator takes an Another interesting point that I found was that public static void OrderByOperators()
{
Book[] books =
{
new Book{Title="ASP.NET 2.0 Website Programming: Problem -
Design - Solution",
Author="Marco Bellinaso"},
new Book{Title="ASP.NET 2.0 Unleashed", Author="Stephen Walther"},
new Book{Title="Pro ASP.NET 3.5 in C# 2008, Second Edition",
Author="MacDonald and Mario Szpuszta"},
new Book{Title="ASP.NET 3.5 Unleashed ", Author="Stephen Walther"},
};
IEnumerable<Book> orderedbooks =
books.OrderBy(b => b.Author).ThenBy(b => b.Title);
}
In the above example, I make use of both public static IOrderedEnumerable<T> OrderBy<T, K>(
this IEnumerable<T> source,
Func<T, K> keySelector,
IComparer<K> comparer);
interface IComparer<T> {
int Compare(T x, T y);
}
The public class LengthComparer : IComparer<string>
{
public int Compare(string title1, string title2)
{
if (title1.Length < title2.Length) return -1;
else if (title1.Length > title2.Length) return 1;
else return 0;
}
}
I have created a custom comparer which compares the public static void OrderByUsingCustomComparer()
{
Book[] books =
{
new Book{Title="ASP.NET 2.0 Website Programming: Problem -
Design - Solution",
Author="Marco Bellinaso"},
new Book{Title="ASP.NET 2.0 Unleashed", Author="Stephen Walther"},
new Book{Title="Pro ASP.NET 3.5 in C# 2008, Second Edition",
Author="MacDonald and Mario Szpuszta"},
new Book{Title="ASP.NET 3.5 Unleashed ", Author="Stephen Walther"},
};
IEnumerable<Book> orderedbooks = books.OrderBy(b => b.Title,
new LengthComparer());
}
AsEnumerable OperatorI found the public static IEnumerable<T> AsEnumerable<T>(
this IEnumerable<T> source);
The prototype operates on the source of public static void AsEnumerableExample()
{
NorthwindDataContext db = new NorthwindDataContext();
var firstproduct = (from product in db.Products
where product.Category.CategoryName == "Beverages"
select product
).ElementAt(0);
Console.WriteLine(firstproduct.ProductName);
}
When you run this query, it would throw an exception saying that public static void AsEnumerableExample()
{
NorthwindDataContext db = new NorthwindDataContext();
var firstproduct = (from product in db.Products
where product.Category.CategoryName == "Beverages"
select product
).AsEnumerable().ElementAt(0);
Console.WriteLine(firstproduct.ProductName);
}
DefaultIfEmptyThe public static void DefaultIfEmptyExample()
{
string[] fruits = { "Apple", "pear", "grapes", "orange" };
string banana = fruits.Where(f => f.Equals("Banana")).First();
Console.WriteLine(banana);
}
The above example throws an exception because the first operator requires that sequence not be empty. Therefore if we were to use public static void DefaultIfEmptyExample1()
{
string[] fruits = { "Apple", "pear", "grapes", "orange" };
string banana =
fruits.Where(f => f.Equals("Banana")).DefaultIfEmpty("Not Found").First();
Console.WriteLine(banana);
}
Another interesting use of public class Category
{
public string CategoryName { get; set; }
}
public class Product
{
public string ProductName { get; set; }
public string CategoryName { get; set; }
}
public static void LeftOuterJoin()
{
Category[] categories = {
new Category{CategoryName="Beverages"},
new Category{CategoryName="Condiments"},
new Category{CategoryName="Dairy Products"},
new Category{CategoryName="Grains/Cereals"}
};
Product[] products = {
new Product{ProductName="Chai",
CategoryName="Beverages"},
new Product{ProductName="Northwoods Cranberry Sauce",
CategoryName="Condiments"},
new Product{ProductName="Butter",
CategoryName="Dairy Products"},
};
var prodcategory =
categories.GroupJoin(
products,
c => c.CategoryName,
p => p.CategoryName,
(category, prodcat) => prodcat.DefaultIfEmpty()
.Select(pc => new { category.CategoryName,
ProductName = pc != null ? pc.ProductName : "No" })
).SelectMany(s => s);
foreach (var product in prodcategory)
{
Console.WriteLine("Category :{0}, Product = {1}", product.CategoryName,
product.ProductName);
}
}
In the example above, I am using History
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||