Graeme Hill's Dev Blog

LINQ queries return queries not data

Star date: 2010.139

The title of this article is a pretty obvious statement, but it's actually pretty easy to forget and it can lead to some painful bugs. Here's a code snippet whose output may seem surprising:

Module Module1

    Sub Main()
        Dim query =
            From name In {"one", "two", "three"}
            Select New User(name)

        Dim x = query.First
        Dim y = query.First

        Console.WriteLine(x Is y)

    End Sub

End Module

Public Class User
    Public Sub New(ByVal username As String)
        Console.WriteLine("Creating user: " & username)
    End Sub
End Class

At first glance it looks like x and y are the same object, but since query is just a query, not an actual collection, the result will be fetched independently each time you call First. When the code is run, the console output looks like this:

Creating user: one
Creating user: one

This shows that the constructor was called twice for the same string, which explains why x and y are actually different objects. In a case like this it is better to put ToList at the end of the query:

Dim users =
    (From name In {"one", "two", "three"}
     Select New User(name)).ToList()

Bugs like this can be particularly problematic when you pass around an object of type IEnumerable and the programmer assumes that they are dealing with a collection, when they are really dealing with a query. So... watch out!