Run you later - Understanding deferred execution in LINQ

Introduction

In this article I’m going to try to explain one of the most misunderstood features of LINQ – deferred execution - and the impact it might have on your code if you don’t fully understand what it means.

What does deferred execution mean?

According to the dictionary the word deferred means delayed or postponed. So deferred execution in LINQ means that the actual execution of a query is postponed to a later time. Let’s say that you’re using LINQ to SQL and want to use the following LINQ query:

Dim cheapProducts = From p In db.Products _
                    Where p.ListPrice < 100 _
                    Select p

It’s easy to assume that the above will execute a query against the underlying database, but it wont. It will just create an IQueryable that has a reference to something that knows how this query can be executed (when it comes to LINQ to SQL this reference will point to an expression tree, but you don’t have to know about that since that’s just an implementation that the LINQ to SQL provider uses), but it will not run the actual query. LINQ has postponed, or deferred, the execution to a later time.

-To a later time, you say. So when is that exactly?

In LINQ the execution of a query is performed when you request the data, for example in a loop.

For Each prod In cheapProducts
  Console.WriteLine("The list price for {0} is {1}", prod.ProductName, prod.ListPrice)
Next

But you might request the data earlier. Take the following query as an example.

Dim productList = (From p In db.Products _
                   Where p.ListPrice < 100 _
                   Select p).ToList()

In this case we request that the result of the query is turned into a List(Of Product). To be able to do that the query has to be executed. But that doesn’t really mean that we didn’t have a deferred execution, it just wasn’t delayed for very long since we immediately request that the data should be turned into a list. This would also be true if we instead of requesting a list would call the Count() extension method or anything else that wouldn’t return an IEnumerable(Of T) or an IQueryable(Of T).

What kind of implications might this have on my code?

For the purpose of this article I’m going to use a Person class that only have a FirstName and LastName property and a little helper method that fills a List(Of T) with a number of persons, which will be used as the data source of our queries. In other words I’m going to use LINQ to Objects, but the same rules apply even if you use any other LINQ provider such as LINQ to SQL or LINQ to XML.

Public Class Person
  Private _firstName As String
  Public Property FirstName() As String
    Get
      Return _firstName
    End Get
    Set(ByVal value As String)
      _firstName = value
    End Set
  End Property

  Private _lastName As String
  Public Property LastName() As String
    Get
      Return _lastName
    End Get
    Set(ByVal value As String)
      _lastName = value
    End Set
  End Property

  Public Sub New(ByVal firstName As String, ByVal lastName As String)
    _firstName = firstName
    _lastName = lastName
  End Sub

  Public Shared Function GetPersonList() As List(Of Person)
    Dim list As New List(Of Person)
    With list
      .Add(New Person("Bart", "Simpson"))
      .Add(New Person("Lisa", "Simpson"))
      .Add(New Person("Maggie", "Simpson"))
      .Add(New Person("Homer", "Simpson"))
      .Add(New Person("Marge", "Simpson"))
      .Add(New Person("Ned", "Flanders"))
      .Add(New Person("Maude", "Flanders"))
      .Add(New Person("Rod", "Flanders"))
      .Add(New Person("Todd", "Flanders"))
    End With
    Return list
  End Function
End Class

Now let’s say we have the following code.

Public Sub Main()
  Dim personList As List(Of Person) = Person.GetPersonList()

  'Get a list of the Simpsons family
  Dim lastName As String = "Simpson"
  Dim persons = From p In personList _
                Where p.LastName = lastName _
                Select p

  lastName = "Flanders"
  For Each pers In persons
    Console.WriteLine("{0} {1}", pers.FirstName, pers.LastName)
  Next
End Sub

Which are the names that will be written to the console? If you guessed the names of the Simpsons, you haven’t paid attention. The query asks for all persons with the last name “Simpson”, but still the name of the Flanders are written out. Remember that the query is not executed when we define it but rather when we ask for the data. Since we change the lastName variable before the query is executed we have in fact changed the query.

The fact that the execution is delayed until we ask for the data also means that we can reuse the same query. Let’s make some slight changes to the last example.

Public Sub Main()
  Dim personList As List(Of Person) = Person.GetPersonList()

  'Get a list of the Simpson's family
  Dim lastName As String = "Simpson"
  Dim persons = From p As Person In personList _
                Where p.LastName = lastName _
                Select p

  For Each pers In persons
    Console.WriteLine("{0} {1}", pers.FirstName, pers.LastName)
  Next
  Console.WriteLine("---------------")
  'Change the last name
  lastName = "Flanders"
  'Reuse the query
  For Each pers In persons
    Console.WriteLine("{0} {1}", pers.FirstName, pers.LastName)
  Next
  Console.ReadLine()
End Sub

In this example we first write out the names of the Simpson family. Then we change the lastName variable and loop through it again. Since the query has been changed it will be executed a second time and we’ll get the names of the Flanders family. We reused the same query to get two different results.

Conclusion

LINQ is wonderful thing. It allows you to do complex things with very few lines of code and that can surely bring a big smile to your face. However, that smile can easily be turned into a stupid looking grin if you don’t understand deferred execution. If you don’t understand exactly when the query will be executed you might present completely erroneous data to your end users.

Have fun!

Published Wed, Sep 9 2009 15:54 by Joacim Andersson

Comments

Wednesday, September 09, 2009 8:57 AM by PimpThisBlog.com

# Run you later - Understanding deferred execution in LINQ - Joacim's view on stuff

Thank you for submitting this cool story - Trackback from PimpThisBlog.com

Wednesday, September 09, 2009 8:59 AM by DotNetShoutout

# Run you later - Understanding deferred execution in LINQ - Joacim's view on stuff

Thank you for submitting this cool story - Trackback from DotNetShoutout

Thursday, September 10, 2009 3:00 PM by DotNetBurner - Linq

# Run you later - Understanding deferred execution in LINQ - Joacim's view on stuff

DotNetBurner - burning hot .net content

Leave a Comment

(required) 
(required) 
(optional)
(required) 
If you can't read this number refresh your screen
Enter the numbers above: