Understanding LINQ compilation
LINQ to Entities compiles the queries you create into something that the EntityClient can understand. You’ve seen one example of this compilation in the “Developing LINQ to Entities queries” section of the chapter in the form of bubble help. You were able to hover the mouse over the CustomerList object and see its type.
The following sections look at compilation in another way. These procedures take you through the process of using a query with the debugger. It’s interesting to see how the debugger handles the query based on the way you create it. In fact, using the debugger as shown in the following procedures will help you gain a much better understanding of the Entity Framework as a whole because you can trace through the tasks it performs in the background for you.
Following an IQueryable sequence
The example shown in the “Developing LINQ to Entities queries” section of the chapter uses the var keyword to create the CustomerList object. The var keyword is also used to create Customer and Purchase. When using the var keyword, you allow the compiler to automatically determine which type to use to satisfy a particular need. However, it’s nice to see this process in action.
Simply running the example leaves some questions unanswered. For example, you may wonder how and when Customer and Purchase are created. Working through the example with the debugger helps you answer these kinds of questions.
Open the ModelFirst example that you worked with in the “Developing LINQ to Entities queries” section of the chapter.
Place a breakpoint at the foreach line so that it looks like this:
Click Start or press F5. The application compiles and runs.
Click Query. The debugger stops the application at the foreach line. There are some interesting things to see at this point.
Choose Debug | Windows | Autos. You’ll see the Autos window shown here:
Notice that even though CustomerList uses var as its type, the actual type is IQueryable. The value of CustomerList is a form of the query you used.
When you open the Results View, you see that there are two members of type System.Data.Entity.DynamicProxies. When working with the Entity Framework, it actually creates a dynamically generated derived type that acts as a proxy for the entity. You can read about these proxies at http://msdn.microsoft.com/data/jj592886.aspx. For now, it’s important to realize that the TestModelFirst.Customers objects don’t actually exist.
Expanding the Results View has automatically created the customers for you, so click Stop.
Perform steps 3 and 4 again to restart the debugger.
Click Step Into or press F11 three times. Visual Studio opens a new file, Customers.cs, and places the instruction pointer on the constructor for the Customers class, as shown here:
Here, the application is actually creating a Customers object. This object includes Purchases, as shown.
Click Step Into or press F11 four times. The debugger takes you back to the original file and highlights the in part of the foreach loop, where it verifies that there is another item to process.
Click Step Into or press F11. The debugger highlights the var Customer part of the foreach loop. Choose Debug | Windows | Locals. You’ll see the Locals window, as shown here:
Notice that Customer is still null. However, the data type shows that var Customer creates a TestModelFirst.Customers type. The compiler has automatically chosen the correct type for the variable.
Click Step Into or press F11. The value of Customer changes to a System.Data.Entity.DynamicProxies entry. The type is correct for the kind of information presented, and you see the individual values for Customer when you click the plus sign next to it.
Click Step Into or press F11 six times. The instruction pointer will end up at the Output.Append() line. Notice that the application doesn’t create the Purchase object as it did the Customer object. That’s because the Purchase object already exists as part of the Customer object.
Click Step Into or press F11 enough times to take the instruction pointer back to the in part of the foreach loop. When you click Step Into or press F11 one more time, the debugger reopens Customers.cs, and you start the process of creating a Customers object again, as described in step 9. You can follow this process at least twice if you created the records described in previous chapters.
Click Stop to end the debugging session. At this point, you know that working with the Entity Framework with IQueryable means creating objects on demand.
Following a List sequence
Working with IQueryable produces one result. However, converting the query to a List and then processing that List produces another. It’s interesting to modify the code slightly to see what happens when you use a List to interact with a LINQ to Entities query. The following procedure does just that.
Modify the query in the ModelFirst example so that it looks like this:
// Obtain the customer list in list form. List<Customers> CustomerList = (from cust in context.Customers select cust).ToList<Customers>();
The result of the query is the same. The only difference is that the output is converted to a List.
Click Start or press F5. The application compiles and runs.
Click Query. The debugger stops the application at the foreach line.
Click Step Into or press F11 four times. You end up at the opening curly brace for the foreach loop. Notice that the debugger didn’t open Customers.cs or interact with the constructor in that file. That’s because the act of converting the query output to a List automatically retrieves the data from the database.
Choose Debug | Windows | Locals. You’ll see the Locals window shown here:
Notice that, even though the CustomerList type is not System.Collections.Generic.List<TestModelFirst.Customers>, the Customer object hasn’t changed from before. It’s still of type TestModelFirst.Customers and contains a System.Data.Enty.DynamicProxies value. The only change that using a List creates is the fact that the data entries are retrieved immediately, rather than as needed. That said, using a List could save time when working with larger datasets. You could always create a thread for the data retrieval process so the user can continue working in the foreground.
Click Stop to stop the debugger.