Appendix A. Understanding LINQ

You’re likely already familiar with the various C# and Visual Basic language innovations and their importance in bringing LINQ to life. But if you haven’t come across LINQ yet, this appendix will introduce you to the project and enumerate its advantages, its requirements in terms of language modifications, and its vision. This is important, because the main query language in Entity Framework is LINQ to Entities, which in the end is a LINQ dialect. Having a solid knowledge of LINQ’s foundations will help you understanding LINQ to Entities queries.

In the first part of this appendix, we’ll cover the reasons why the Microsoft architects decided to embed query extensions into the language, and the broad vision of the LINQ platform. After that, we’ll introduce the features that have made LINQ a success. These features are the language innovations that have been introduced in C# 3.0 and VB 9. There are lots of innovations to cover, so this will be a long discussion.

By the end of the appendix, you’ll be able to fully understand and write any LINQ query.

A.1. Why was LINQ created?

Each data technology has its own language for performing queries. Data from databases can be pulled out using SQL commands; XML data can be retrieved using the XPath syntax; even Active Directory and ADO.NET DataTable information can be accessed using their own syntaxes.

Whatever the data source is, retrieving data requires that you have specific knowledge of its query language and related .NET framework classes, as shown in figure A.1. Although this is an obvious consequence of adopting different technologies, a common way of retrieving data is still desirable.

Figure A.1. Searching data from different sources without LINQ requires a wide knowledge of retrieval methods.

LINQ bridges this gap between data technologies and code development by providing a set of common extensions that can be used to query any data source. These extensions aren’t related to a specific data technology but can be used across all of them, creating a unique language for all data sources.

Another point that makes LINQ a great innovation is its way of shaping query results. When you retrieve data from a database, they’re returned in DataTable or DbDataReader objects. The same happens for XML data, which is represented as XmlNode objects.

The search for a common coding style can’t ignore such diversity. Not only does LINQ enable a brand-new opportunity for standardizing the query language, but it also returns the results of a query in a single well-known format: in objects. This is another significant step toward flattening the data access differences, as figure A.2 illustrates.

Figure A.2. Data access using LINQ technology

The real power of LINQ lies in its extensibility. Versions 1.0 and 1.1 of the .NET Framework class library had many features that were provided as black boxes, where you couldn’t plug in or modify any behavior. Fortunately, lots of things have changed since version 2.0, and one of the guidelines has become extensibility. For instance, the provider model was introduced so that you can change the underlying behavior of a feature without touching its interface. And many classes that were sealed have been opened for inheritance, offering developers much more flexibility and freedom.

The current version of the .NET Framework class library has continued this trend, and LINQ has a provider-based architecture that allows you to write providers to query any data source. .NET Framework 3.5 introduced four flavors of LINQ:

  • LINQ to Objects—Queries in-memory lists of objects
  • LINQ to XML—Queries XML files or in-memory structures
  • LINQ to SQL—Queries the domain model generated by the LINQ to SQL O/RM
  • LINQ to DataSet—Queries data in all the DataTables of a DataSet

The evidence of LINQ’s extensibility lies in the myriad of providers that have been created to query any data source. Further proof is the LINQ to Entities provider, which enables you to query models based on Entity Framework.

LINQ has also introduced a conceptual change. When you write a LINQ query, you express what you want to retrieve, but you don’t specify how to do that. That’s a big shift for .NET languages, because for the first time, they introduce some of the functional programming concepts. When you perform a SQL query, for example, you have no idea of how the database will execute it; you just state what you want, and leave the search to the internal system. The same thing happens with LINQ.

To make this concept clearer, look at the following snippet:

C#

var result = from o in orders
             where o.OrderDate.Date == DateTime.Now.Date
             select o;

VB

Dim result = From o in orders _
             Where o.OrderDate.Date = DateTime.Now.Date

This query filters orders, returning only those placed today. You don’t specify anything about the retrieval method, and you don’t know how LINQ will perform it.

 

Note

LINQ uses a simple in-memory loop over the orders, but that’s completely hidden from you. The LINQ implementation could change one day, and you wouldn’t notice it, nor would you need to modify the code.

 

The LINQ technology has its foundations in language innovations. Most of the new features that have shown up in C# 3.0 and VB 9 are there because LINQ requires them. In this appendix, we’ll provide a brief introduction to LINQ and the language improvements, but you can find plenty of books and online material that discuss this in great detail. For further information, we strongly recommend the book LINQ in Action by Fabrice Marguerie, Steve Eichert, and Jim Wooley (Manning, 2007).

The advent of LINQ brought about improvements in the following areas:

  • Type inference
  • Extension methods
  • Lambda expressions
  • Object initializers
  • Anonymous types
  • Query syntax

We’ll look at these topics in the following sections.

A.2. Type inference

Type inference is the new ability of the compiler to understand the type of an object without you having to explicitly declare it. To let you declare a variable without specifying the type, C# offers the var keyword. VB doesn’t need a new keyword, because it already has one that can be used for the same purpose: Dim. The compiler automatically infers the type of the variable from the expression used to initialize it.

In C# it’s mandatory to initialize a variable; otherwise, the compiler wouldn’t be able to understand the type automatically and would throw a compile-time exception. In VB, a variable that isn’t initialized becomes of type Object. The following listing shows this behavior.

Listing A.1. How type inference understands an object’s type

The code generated by the compiler is exactly the same for the first two blocks of each of these snippets , because it infers the types from the values assigned to the variables when they’re declared. The x variable declaration in C# throws an exception because it’s initialized to null, so the type can’t be inferred. The x variable declaration in VB is valid because the type of the x variable is Object.

In C#, using the var keyword comes in handy when you have to declare a complex type with many generic declarations, as in the following code:

Dictionary<string, Dictionary<int, Order>> variable = new
  Dictionary<string, Dictionary<int, Order>>();

Repeating the type for such a long piece of code is at least annoying. Using var allows you to write the type only once, in the assignment part of the statement:

var variable = new Dictionary<string, Dictionary<int, Order>>();

In addition to making your code smaller, var can also make it less readable. C# developers are used to seeing the type of a variable at the beginning of the declaration. Cluttering the code with var forces developers to read the full statement to determine the variable’s type, and some developers argue that this is a waste of time. Apart from a few cases that you’ll see later in this appendix, using var or declaring the variable type is a matter of personal taste.

 

Note

Be aware that the var keyword in C# has nothing to do with the var keyword in JavaScript. The var keyword in C# allows neither late binding nor VB variant-like features.

 

We’ve only scratched the surface of type inference. As you’ll see in the next sections (in particular, when we talk about lambda expressions), this feature allows you to cut even more code than you’ve seen so far.

A.3. Extension methods

An extension method is a method that’s defined in a class but is invoked using another class to which it’s attached by the compiler. This definition might seem a bit awkward, but the use of extension methods will be clear enough when we look at some examples.

Suppose you have a list of orders, and you want to serialize those created on a specified date in JSON format. Without using extension methods, the only way to go is to write a method that accepts the orders as input and iterates over them, serializing those that correspond to the requirement, and returning the JSON string when the iteration is done. The code would look like this.

Listing A.2. Creating and using a helper method without extension methods

C#

namespace EFInaction.Common
{
  public static class Utilities
  {
    public static string SerializeOrders(List<Order> orders, DateTime date)
    {
      JavaScriptSerializer serializer = new JavaScriptSerializer();
      StringBuilder builder = new StringBuilder();
      foreach(var order in orders)
       {
        if (order.OrderDate.Date == date.Date)
          builder.Append(serializer.Serialize(order));
       }
      return builder.ToString();
    }
  }
}

namespace EFInaction.UI
{
  ...
  List<Order> orders = ctx.Order.ToList();
  string serializedOrders = Utilities.SerializeOrders(orders, DateTime.Now);
  ...
}

VB

Namespace EFInAction.Common
  Class Utilities
    Public Shared Function SerializeOrders( _
       ByVal orders As List(Of Order), _
      ByVal now As DateTime) As String
      Dim serializer As New JavaScriptSerializer()
       Dim builder As New StringBuilder()
      For Each order In orders
        If order.OrderDate.Date = now.Date Then
          builder.Append(serializer.Serialize(order))
        End If
      Next
      Return builder.ToString()
    End Function
  End Class
End Namespace

Namespace EFInAction.UI
  ...
  Dim orders = ctx.Order.ToList()
  Dim serializedOrders = EFInAction.Common.Utilities.SerializeOrders( _
    orders, DateTime.Now)
  ...
End Namespace

The code in listing A.2 is fine, except that the caller code has to resort to calling another class to perform steps that are closely related to the orders. Wouldn’t you feel more comfortable if you could invoke the SerializeOrders method using the orders instance instead the Utilities class?

By using extension methods, you can invoke SerializeOrders on the orders instance even if the method is defined in another class, as is demonstrated in the following listing. This is a modified version of listing A.2.

Listing A.3. Creating and using a helper method with extension methods

In the method declaration , you have to indicate what type the method must be attached to. In VB, this is accomplished by specifying in the first parameter the class that’s extended, and adding the Extension attribute to the method. In C#, you only need to add the this keyword before the extended type without using any attribute. If the method accepts other parameters, they must be added after the first parameter; if the type isn’t first, you’ll get a compile-time error. There is one caveat in both languages: in VB, an extension method can be defined only in a module; and in C#, it must be defined in a static class, and the method must be static too.

In the caller code, the first thing you have to do is to add a using statement for C# (Imports for VB) to import the namespace that contains the class that declares the extension method. Without that, the compiler can’t detect the class and can’t apply the extension method. As shown in figure A.3, Visual Studio offers autocompletion for extension methods; but if you don’t use the using or Imports keyword, they don’t even appear in the autocompletion list.

Figure A.3. Visual Studio autocompletion without and with the using statement

The second change you have to apply to the caller code is to invoke the extension method from the orders object . The parameters for the method must be modified too. The parameter for the extended class must be omitted, and only those that come after it should be included.

To enable extension methods in LINQ, you must add the System.LINQ namespace to the class. This namespace contains the Enumerable class, which in turn contains all the LINQ extension methods.

In section A.1, we said that LINQ was been conceived as a set of extensions to the VB and C# languages for querying any data source. At the highest level, you can think about LINQ as a set of extension methods that you can use to perform queries. Table A.1 lists all the LINQ methods grouped by type.

Table A.1. LINQ methods grouped by type

Type

Methods

Projection Select, SelectMany
Filtering Where
Sorting OrderBy, OrderByDescending, Reverse
Grouping GroupBy
Set Distinct, Union, Intersect, Except
Generation Range, Repeat, Empty
Conditional Any, All, Contains
Element Last, LastOrDefault, ElementAt, ElementAtOrDefault, First, FirstOrDefault, Single, SingleOrDefault, SequenceEqual, DefaultIfEmpty
Paging Take, TakeWhile, Skip, SkipWhile
Aggregation Count, LongCount, Sum, Min, Max, Average, Aggregate

What’s great about LINQ methods is that they enable the fluent technique so they can be combined in a single statement.

A.3.1. Method chaining

The power of extension methods becomes clear when you combine them to perform a more complex query. Suppose you have to sort the orders before serializing them. You could perform this operation in the SerializeOrders method, but if you also need to sort orders in other parts of your application, this isn’t the most convenient approach.

This is the optimal solution:

  1. Create an extension method that sorts the orders and returns the ordered list.
  2. Invoke the SerializeOrders method to serialize the objects.

With this approach, you achieve the goal of code reuse. Because the sorting method returns a list of orders, you can invoke the SerializeOrders method on it in the same line of code (which is why this technique is named method chaining). Here’s an example:

C#

var x = orders.Sort().SerializeOrders(DateTime.Now);

VB

Dim x = orders.Sort().SerializeOrders(DateTime.Now)

LINQ offers a wide range of extension methods. Most of the methods extend the IEnumerable<T> interface, which is directly or indirectly implemented by all generic lists in the .NET Framework, enabling query capabilities on any set of objects. Because most of the methods return an IEnumerable<T> instance, method chaining is a common pattern when writing LINQ queries.

Now that you know how LINQ enables method chaining, let’s discuss how extension methods are evaluated by the compiler.

A.3.2. Method evaluation

When you design an extension method, you have to know how the compiler evaluates the calls to it. Let’s start with an example.

Listing A.4. A set of extension methods that add methods to different classes

C#

public static class Extensions
{
  public static string ExtMethod(this List<Order> orders)
  {
    return "Method on List<Order>";
  }

  public static string ExtMethod(this IEnumerable<Order> orders)
  {
    return "Method on IEnumerable<Order>";
  }

  public static string ExtMethod<T>(this List<T> orders)
  {
    return "Method on generic List";
  }
}

VB

Module Extensions
  <Extension()> _
  Public Function ExtMethod(ByVal orders As List(Of Order)) As String
    Return "Method on List<Of Order>"
  End Function

  <Extension()> _
  Public Function ExtMethod(ByVal orders As IEnumerable(
    Of Order)) As String
    Return "Method on IEnumerable<Of Order>"
  End Function

  <Extension()> _
  Public Function ExtMethod(Of T)(ByVal orders As List(Of T)) As String
    Return "Method on generic List"
  End Function
End Module

The class in listing A.4 contains three extension methods that extend three different classes. Let’s see how the compiler resolves the calls, starting with the following code:

C#

List<Order> o1 = new List<Order>();
var x = o1.ExtMethod();

VB

Dim o1 as New List(Of Order)
Dim x = o1.ExtMethod()

All of the extension methods in listing A.4 are suitable for being executed by the call to the o1.ExtMethod in the preceding example, but only one is actually executed. To identify the method that must be executed, the compiler checks the object to be extended and uses the one closest to the variable definition of the object in the calling code, starting from the bottom of the inheritance hierarchy.

In this case, because the o1 variable is of type List<Order> and the first extension method extends that type, it’s the one that’s attached.

Now let’s see what method is invoked by the next snippet:

C#

IEnumerable<Order> o2 = new List<Order>();
var x2 = o2.Method();

VB

Dim o2 as IEnumerable(Of Order) = New List(Of Order)()
Dim x2 = o2.Method()

Here you have a situation where the real object is List<Order>, but it’s put into an IEnumerable<Order> variable. As we’ve said, the compiler uses the variable declaration, so in this case the second extension method of listing A.4 is invoked even if the first would be more appropriate.

Here’s one more example:

C#

List<Company> o3 = new List<Company>();
var x3 = o3.Method();

VB

Dim o1 as New List(Of Company)
Dim x3 = o3.Method()

Here, the second and the third methods of listing A.4 would be suitable; but because List<T> is lower in the inheritance hierarchy, the third method is used by the compiler.

 

Note

All of this method resolution is resolved at compile time. There’s nothing dynamic at runtime.

 

As you learned in chapter 4, this resolution pattern turns out to be useful when writing LINQ to Entities queries that use other LINQ standard operators.

A.4. Lambda expressions

A lambda expression is the evolution of the anonymous method feature introduced in .NET 2.0. It’s difficult to explain what a lambda expression is, so we’ll discuss it using examples and comparisons with its predecessor.

A.4.1. Anonymous methods

Before digging into the lambda expression world, let’s start with the beginning. In .NET 1.0, the only way to execute a piece of code was to create a method like the following one.

Listing A.5. A method that searches orders by date

C#

OrderCollection GetOrdersByDate(OrderCollection orders, DateTime date)
{
  OrderCollection result = new OrderCollection();
  foreach(Order order in orders)
  {
    if (order.OrderDate.Date == date.Date)
     {
      result.Add(order);
     }
  }
  return result;
}

VB

Private Function GetOrdersByDate(ByVal orders As OrderCollection, _
  ByVal theDate As DateTime) As OrderCollection
    Dim result As New OrderCollection()
    For Each order As Order In orders
        If order.OrderDate.Date = theDate.Date Then
            result.Add(order)
        End If
    Next
    Return result
End Function

Likely, you’re quite comfortable with the code in listing A.5 and see no problems with it, and indeed there aren’t any. But with the advent of .NET 2.0 and its anonymous methods, there is a lot that can be optimized in this code.

After refactoring to use the new features, the code from listing A.5 looks like this.

Listing A.6. Searching orders by date using anonymous methods

C#

DateTime date = DateTime.Today;
List<Order> result = orders.Find(delegate (Order order)
  {
    return order.OrderDate.Date == date.Date;
  }
);

VB

Dim theDate As DateTime = DateTime.Today;
Dim result As List(Of Order) = orders.Find(Function(ByVal order As Order) _
  order.OrderDate.Date = theDate.Date)

The amount of code you need to write is reduced a lot. The Find method handles most of the plumbing code in the previous version, as it iterates over the items and automatically passes each one to the code expressed in form of a predicate.

The predicate is the piece of code you put into the Find method. It decides whether the current object must be included in the result or not. The predicate lets you express the filter that must be applied; in the end, the filter is the only thing you should care about.

This syntax is far more concise than the original in listing A.5, but it still suffers from some verbosity because there was no type inference in the C# 2.0 compiler. For instance, because the list contains Order instances, there’s no point in repeating this in the predicate. C# 3.0 adds type inference, but instead of improving anonymous methods, the team opted to include a brand-new feature called lambda expressions.

A.4.2. From anonymous methods to lambda expressions

Now that you’ve had a quick refresher on coding before .NET 3.5, you’re ready to dive into lambda expressions and understand their logic. Let’s start by refactoring the example in listing A.6 using lambda expressions like in figure A.4.

Figure A.4. The structure of a LINQ query that uses a lambda expression

There’s probably no way you can write less code than what you see in figure A.4. The syntax is different from what you were used to in previous versions of C# and VB. The left part of the statement (the part before the = sign) uses the type inference to understand the output of the right part of the statement (the part after the = sign). It’s not mandatory to use type inference here because you know exactly what the type of the output is: IEnumerable<Order>. That means you can replace var with the concrete type.

Where is a LINQ extension method that filters the list on the basis of the statement that’s passed as input. The statement that the method receives in input is the lambda expression.

 

Note

Sometimes LINQ extensions methods are also referred to as operators or clauses.

 

In C#, a lambda expression is formed by two main blocks separated by the lambda operator. The left block declares the input parameters for the lambda expression. You don’t have to explicitly set the type of the parameter because the compiler relies once again on type inference to correctly identify it. Because the list that must be filtered contains Order instances, in this case, the input variable type (o) is of type Order. If the lambda expression accepts more than one parameter, they must be separated using a comma, and the left part must be surrounded by brackets, as in the following snippet:

var result = orders.Where((o, i) => o.OrderDate.Date == date.Date && i
  > 10);

The block at the right of the lambda operator contains the lambda expression that’s applied to each object to determine whether it should be included in the result. The difference between the lambda expression–based code and the anonymous method–based code is that the return keyword is omitted. The C# team has enabled you to omit the return keyword to further reduce code.

In VB, the lambda expression is introduced by the Function keyword and includes the parameters in parentheses. Furthermore, unlike in C#, the lambda operator doesn’t exist. The rest of the syntax matches the C# syntax.

To fully understand lambda expressions, let’s look at how a method that accepts a lambda expression is declared using the Where method as an example.

Listing A.7. Anatomy of an extension method

As you can see, Where is a function, so the first thing you need to do is declare the output type. Because the Where method filters data without modifying it, its return type is the same as its input.

The Where method accepts a generic parameter that is the type of the elements contained in the collection the Where method filters. The predicate parameter represents the lambda expression. Func is a class of the .NET Framework that lets you declare a delegate concisely. It accepts from 2 to 17 generic parameters. Regardless of how many parameters you use, the last parameter represents the output type and the others are input types. In this example, TSource is the input type (Order), and bool is the result of the expression.

Now that you have a clear understanding of lambda expressions, we can move on to another interesting feature: object initializers.

A.5. Object initializers

When you have to instantiate an object and initialize its properties, you have two methods at your disposal. If the class provides a constructor that accepts properties to initialize values, you can use that. Alternatively, you can use one of the other constructors and initialize the properties one by one, as shown here:

C#

Order o = new Order();
o.Id = 1;
o.Address = "5th Street, 234";

VB

Dim o as New Order()
o.Id = 1
o.Address = "5th Street, 234"

Writing code like this is tedious and redundant. In .NET Framework 3.5, the C# and VB teams introduced object initializers. An object initializer allows you to instantiate an object and set its properties in a single statement. With object initializers, you can drastically reduce the number of lines of code with a convenient syntax:

C#

Order o = new Order { Id = 1, Address = "5th Street, 234" };

VB

Dim o As New Order With { .Id = 1, .Address = "5th Street, 234" }

When the compiler intercepts the preceding statement, it generates the same code created by the previous snippet. Both approaches produce the same result with exactly the same performance.

 

Note

By using tools like Reflector, this matching performance can be easily verified. If you look, you’ll see that both examples produce the same IL code. Because Reflector relies on IL to regenerate original code, if you switch to C# or VB view, you’ll see that the code looks like the first snippet in this section.

 

By default, the compiler uses the constructor with no parameters to instantiate the class. If that constructor isn’t available, you’ll have to use one of the available constructors and then open the curly brackets to set the properties, as in the following code:

C#

Order o = new Order(1) { Address = "5th Street, 234" };

VB

Dim o As New Order(1) With { .Address = "5th Street, 234" }

The object initializers offer a practical syntax and are very useful. But the real reason for their introduction is that they lay the foundation for the anonymous types feature.

A.6. Anonymous types

The anonymous types feature enables you to create objects without declaring a class beforehand. Because the newly created object doesn’t have a class, the var keyword is the only way to declare it, and the object initializers offer the syntax to create properties.

Here’s an example:

C#

var x = new { Id = 1, Address = "5th Street" };

VB

Dim x = New With {.Id = 1, .Address = "5th Street"}

The x variable contains an instance of a class with two properties: Id and Address. The types of the properties are automatically inferred using type inference.

One point must be clear when dealing with anonymous types: anonymous types can’t be used to generate types dynamically. The types are generated by the compiler during compilation, so nothing is dynamic at runtime.

Figure A.5 shows how the class created in the previous snippet is generated by the compiler.

Figure A.5. The skeleton of the anonymous type generated by the compiler

Anonymous types suffer from some nasty limitations:

  • The object has the scope of the method where it’s generated. When you instantiate an anonymous object, it doesn’t have an explicit type that can be specified as input or output for other methods. The name is generated only at compile time, so there’s no way to use it outside the method that generated it.
  • In C#, the object is immutable. The type in figure A.5 shows a peculiarity of anonymous types in C#. When the compiler generates the properties of an anonymous type, only their getter methods are created. As a result, any instance of this anonymous type can’t be modified after it has been created.

Object initializers and anonymous types are part of the foundation of LINQ. Their combined use allows you to process a list with a defined source and return another list made of other objects generated with data from the input source. As a result, these features enable projections like the one in the following snippet:

C#

var x = orders.Select(o => new { o.OrderId, o.ShippingAddress });

VB

Dim x = orders.Select(Function(o) New With { .o.OrderId, _
  o.ShippingAddress })

Here the Select method is used to make a projection that creates a list containing anonymous objects with the ID and shipping address.

Writing LINQ queries is easy with the features we’ve looked at so far, but there’s another way of doing it. Because almost all developers know the SQL language, LINQ has a set of statements that allows you to write queries using a SQL-like syntax.

A.7. Query syntax

The last LINQ-related improvement made to the C# and VB languages is the query syntax. This particular feature avoids using extension methods and lambda expressions and instead offers a syntax closer to SQL. Here’s an example:

C#

var x = from o in orders
        where o.OrderDate.Date == DateTime.Now.Date
        select o;

VB

Dim x = From o In orders
        Where o.OrderDate.Date = DateTime.Now.Date

Although there are significant differences between this and the SQL language, this way of writing queries is far more readable than the combination of extension methods and lambdas.

 

Note

The most noticeable difference between SQL and this query syntax is the position of the select clause. It’s at the end for autocompletion purposes. Specifying the from clause at the beginning allows Visual Studio to provide autocompletion for all the following clauses. Except for a grouping query, the select clause is mandatory for query syntax in C#; for VB, it’s optional and implicitly returns the type specified in the from clause.

 

The query syntax is pure syntactic sugar because at compile time the code is converted to use extension methods and lambda expressions.

You have to keep in mind that the query syntax doesn’t cover all that the LINQ extension methods offer. If you need to use a method that isn’t supported by the query syntax, you’ll have to resort to extension methods and lambda expressions. Furthermore, VB and C# support different keywords, so there are many differences between the languages.

A.8. Deferred execution

LINQ is lazy by nature. If you start the debugger and execute a query block, you aren’t really executing anything. Although this may seem strange, it’s pretty obvious in a lazy environment. When you execute a query, you aren’t using its result unless you invoke a method that forces result creation. The LINQ query gets executed only when you fetch the data it returns, such as when you use a foreach, use a for, refer to an object in a list by index, or invoke methods like ToList, ToArray, First, Single, and so on.

The following listing contains some examples of query execution.

Listing A.8. Deferred and immediate execution of a LINQ query

When using LINQ to objects, you may not spot the difference; but when you’re using LINQ to Entities, knowing this behavior may spare lots of resources, as you learned in chapter 3 when we talked about inadvertent query execution.

A.8.1. Runtime query composition

Deferred execution is important because it enables scenarios where you compose a query in different steps. Often, queries aren’t fixed but are created depending on runtime conditions.

Consider a search form that accepts many optional parameters. The search condition isn’t fixed because the optional parameters aren’t determined. This requires a set of conditional statements and the construction of a query based on data the user enters. The following listing shows an example of this.

Listing A.9. Composing a query at runtime

C#

var result = orders.AsQueryable();

if (year.Text != String.Empty)
  result = result.Where(o => o.OrderDate.Year ==
    Convert.ToInt32(year.Text));

if (companyName.Text != String.Empty)
  result = result.Where(o => o.Company.Name.StartsWith(companyName.Text));

var data = result.ToList();

VB

Dim result = orders.AsQueryable()

If year.Text <> String.Empty Then
  result = result.Where(Function(o) o.OrderDate.Year = _
    Convert.ToInt32(year.Text))
End If

If companyName.Text <> String.Empty
  result = result.Where(Function(o) _
    o.Company.Name.StartsWith(companyName.Text))

Dim data = result.ToList()

This feature is another main piece of the LINQ architecture that you have to keep in mind while writing queries.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.72.245