Showing posts with label Linq. Show all posts
Showing posts with label Linq. Show all posts

Monday, November 8, 2010

LINQ Cast & LINQ over Dataset: Part 2

1. When to use OfType & Cast
2. What support C# provides to query over datasets and data tables (LINQ to dataset).
3. Implications of anonymous types.

OfType & Cast


public static void CastOperator()
{
ArrayList list = new ArrayList();
list.Add(1);
list.Add(2);
list.Add("ASP.NET 3.5 Unleashed");


IEnumerable numbers = list.Cast();
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 OfType operator will return objects that can be cast to the right type and skips the rest from the collection.

public static void OfTypeOperator()
{
ArrayList list = new ArrayList();
list.Add(1);
list.Add(2);
list.Add("ASP.NET 3.5 Unleashed");


IEnumerable numbers = list.OfType();
foreach (int number in numbers)
{
Console.WriteLine(number);
}
}

LINQ Over Dataset

Out-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 DataTable extensions, DataRow extensions and TypedTableBasedExtesions. To make a data table LINQ aware, you simply need to call the AsEnumerable extension, which will return an EnumerableRowCollection consisting of DataRows. Another very useful extension method is DataRowExtensions.Field(), which allows you to do safe casting in a LINQ query and helps prevent runtime exceptions. Once you apply the filter on a datatable using the LINQ query, the result is simply EnumerableRowCollection. In order to return back a table, we can use the new copytodatatable extension method on the result.


public static void DataSetExample(DataTable books)
{
var implicittype = from book in books.AsEnumerable()
where book.Field("Author") == "Stephen Walther"
select book;
var datatable = implicittype.CopyToDataTable();
}


Anonymous Types
The 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 var keyword. Anonymous types allow you to define class on-the-fly using object initialization syntax and assigning to var keyword. At compile time, the C# compiler will generate a unique class which is not accessible from your code. All anonymous classes derive from System.Object and therefore inherit all the properties and methods of the object class. Anonymous classes are provided with overridden versions of Equals(), GetHashCode and ToString(). The ToString implementation for an anonymous book class would look something like this:


public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}




public override string ToString()
{
StringBuilder builder = new StringBuilder();
builder.Append("{ BookTitle = ");
builder.Append(this.i__Field);
builder.Append(", BookAuthor = ");
builder.Append(this.i__Field);
builder.Append(" }");
return builder.ToString();
}

LINQ Execution

How LINQ leverages differed execution and what benefits and impacts it can have to a developer.

Differed and Immediate Execution

By 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 ToArray<T>(), ToDictionary<TSource, TKey>() and ToList<T>() to capture the results in a strongly typed container which would not reflect new changes made to the original collection. Here is an example:

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} <>, 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} <>,  n);
        }
 
        //immediate execution
        numbers = new int[] { 1,  2,  3,  4,  5,  6 };
        var lessthan4immediate = numbers.Where(n => n < 4).Select(n => n).ToArray();
        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} <>,  n);
        }
    }


As you can see that, 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. However, the query does not run until you iterate over the collection.

One of the downsides I have observed with differed execution is that bugs are harder to trace because enumerating the query could happen much further down in your code. This is where the exception would be thrown. However, you may forget that it is the original query that is the problem. So if differed execution is not the desired behavior you want, use the non-deferred extension methods like ToArray, ToList, ToDictionary or ToLookup to force the query to be executed. Let's see an example for that:


public static void DelayedExceptions()
    {
        string[] blob = {"Resharper", "is", "great"};
        IEnumerable sentence = blob.Select(s => s.Substring(0,  5));
        foreach (string word in sentence)
        {
            Console.WriteLine(word);
        }
    }