[This post is part of a series on LINQ. The index is here and you can download the code samples here.]
Now we're going to introduce a LINQ feature that's confusing at first but soon becomes so natural that you forget all about it: deferred execution.
Consider the following code taken from Program.cs in the Demo2 project. It employs LINQ to Entities to query a SQL Server database using the Entity Framework.
static void Main(string[] args)
{
using (var context = new LinqDemoEntities())
{
var largest = GetLargest(context, 15);
foreach (var city in largest)
Console.WriteLine("{0,-30}{1,11:###,###,##0}", city.Name, city.Population);
}
Console.WriteLine();
Console.WriteLine("Press Enter to quit.");
Console.ReadLine();
}
static IEnumerable<City> GetLargest(LinqDemoEntities context, int numDesired)
{
#region Generated SQL
// SELECT TOP (15)
// [Extent1].[Name] AS [Name],
// [Extent1].[StateCode] AS [StateCode],
// [Extent1].[Population] AS [Population]
// FROM [dbo].[Cities] AS [Extent1]
// ORDER BY [Extent1].[Population] DESC
#endregion
return context.Cities
.OrderByDescending(c => c.Population)
.Take(numDesired);
}
If you're not familiar with the Entity Framework, all you need to know is that the context object manages the interaction with the database, and its Cities property (last statement in the code) accesses the Cities table.
You might think that the database is hit in that last line of code, but you would be wrong!
If you were to use the SQL Profiler to see when the database hears you coming, it would not be until you start to peel off the results -- in the foreach loop!
Why would LINQ's designers make it work this way? One big benefit is that since the construction of the query is distinct from its execution, you're able to build queries piece-by-piece. That will be our subject in Part 6, on Expressions.
In the meantime, let's continue to Part 4, which extolls LINQ's virtues as a universal query language.