In my last two posts (here and here), I showed how you can pre-compile a LINQ query to improve your performance. As I noted in those posts, I wouldn’t expect a blinding improvement in response times—your application probably doesn’t have many really complex queries and, as a result, probably doesn’t spend a lot of time converting LINQ into SQL.
What I didn’t say explicitly is that what the pre-compile process does is generate a delegate—which means that, once you’ve pre-compiled your LINQ statement, you can call it asynchronously, as you would with any other delegate. If you have a long running query, you might prefer to have your LINQ statement execute asynchronously so that your user interface isn’t tied up while you wait for results to be returned (or so your application can go and do something else). While simply pre-compiling your LINQ statements won’t save you a lot of time, running your statements asynchronously is a change that could give you a more responsive application.
Assuming you’re familiar with the material in the previous posts, at this point you’ll have a variable called qry that holds a reference to a pre-compiled LINQ statement. To execute the LINQ statement and catch its results (a collection of Northwind Order objects for a specific customer), you’d call the compiled query’s Invoke method, passing whatever parameters are required. For my example, which requires the LINQ statement to be passed its ObjectContect and a customer Id, that code looks like this:
Dim oc As New northwndModel.northwndEntities
Dim res = qry.Invoke(oc, “ALFKI”)
For Each ord inres
The problem here is that, even with the SQL statement already generated, you still have to wait for Entity Framework to open a connection to the database, send the SQL, wait for the data to be retrieved, and (finally) for the database connection to be closed. Your application could be doing something else if you executed the statement asynchronously.
To execute your LINQ statement asynchronously, you call the BeginInvoke method on the variable holding the compiled statement instead of using the Invoke method. The parameters passed to the BeginInvoke method are the same as those passed to the Invoke method with the addition of parameter specifying a method to call when the LINQ query completes (and a fourth parameter set to Nothing).
This example, for instance, specifies that the method ProcessOrders is to be called automatically when the LINQ statement completes:
qry.BeginInvoke(en2, “ALKFI”, AddressOf ProcessOrders, Nothing)
The ProcessOrders method must accept as its only parameter an IAsyncResult object. To retrieve the result of the LINQ statement, you call the EndInvoke method on the variable holding your compiled LINQ query, passing that IAsyncResult object. The EndInvoke method will return the result of your LINQ query. This example extracts the collection of Orders generated by the LINQ statement:
Public Sub ProcessOrders(iar As IAsyncResult)
Dim ords = qry.EndInvoke(iar)
A LINQ statement is “lazy loaded”—the actual data isn’t retrieved until you process the results from the LINQ statement. You should probably alter the lambda expression holding your LINQ statement to ensure that the data is retrieved on the background thread. You can do that by converting the results of your compiled statement into a List before returning it. If you’re doing that conversion, you should change the data type of the final data type passed to the Compile method since it represents the type being return from the expression:
Dim qry = System.Data.Objects.CompiledQuery.Compile(
List(Of northwndModel.Order)) _
(From o In en.Orders
Where o.CustomerID = custid
There are trade-offs to be made here. If, for instance, you only process some of the Orders retrieved by this statement then whatever benefits you may gain by running asynchronously may be lost by retrieving too much data.
I mentioned in my last post which Learning Tree course had the best coverage of LINQ. I should also have mentioned the course that I think does the best job on Entity Framework: Learning Tree’s Design Patterns and Best Practices. A course, by the way, that I think every .NET developer should take.