PLINQO for LINQ to SQL
Future Queries
With PLINQO, the future is now! Build up a list of queries for the data that you need and the first time any of the results are accessed, PLINQO will retrieve all the data in one round trip to the database server. Reducing the number of trips to the database is a great optimization that makes for smoother, faster running applications. Using this feature is as simple as appending .Future() to the end of your queries. To use the Future Queries please make sure that you are importing the CodeSmith.Data.Linq Namespace. Here is a quick sample.
// build up multiple queries var q1 = db.User .ByEmailAddress("one@test.com") .Future(); var q2 = db.Task .Where(t => t.Summary == "Test") .Future(); // this triggers the loading of all the future queries var users = q1.ToList();
No data is retrieved until q1.ToList(); is executed. At that time, PLINQO knows to execute all the future queries automatically. The data is both batched and not retrieved until it is needed.
Not only can you queue up execution of queries for the future, the results can be cached as well. Again, PLINQO makes things easy. Here's a quick look at FutureCache.
// cache these results for 120 seconds var q1 = db.User .ByEmailAddress("one@test.com") .FutureCache(120); var q2 = db.Task .Where(t => t.Summary == "Test") .FutureCache(120); // this triggers the loading of all the future queries var users = q1.ToList();
Queuing up for the future has never been easier!
The Details
Future queries are created with the extension methods Future(), FutureFirstOrDefault(), or FutureCount().
var db = new TrackerDataContext(); // build up queries var q1 = db.User .ByEmailAddress("one@test.com") .Future(); var q2 = db.Task .Where(t => t.Summary == "Test") .Future(); // this triggers the loading of all the future queries var users = q1.ToList();
In the example above, there are 2 queries built up, as soon as one of the queries is enumerated, it triggers the batch load of both queries.
var db = new TrackerDataContext(); // base query var q = db.Task.ByPriority(Priority.Normal); // get total count var q1 = q.FutureCount(); // get page var q2 = q.Skip(pageIndex).Take(pageSize).Future(); // triggers sql execute as a batch int total = q1.Value; var tasks = q2.ToList();
In this example, we have a common senerio where you want to page a list of tasks. In order for the GUI to setup the paging control, you need a total count. With Future, we can batch together the queries to get all the data in one database call.
Future Query Cache
Future queries can also be cached using the same syntax of FromCache extension method. Each query is cached separately to provide more flexibility.
var db = new TrackerDataContext(); // cache for 120 seconds CacheSettings cache = new CacheSettings(120); // build up queries var q1 = db.User .ByEmailAddress("one@test.com") .FutureCache(cache); var q2 = db.Task .Where(t => t.Summary == "Test") .FutureCache(cache); // this triggers the loading of all the future queries var users = q1.ToList();
How It Works
Future queries work by creating the appropriate IFutureQuery object that keeps the IQuerable. The IFutureQuery object is then stored in IFutureContext.FutureQueries list on the DataContext that created the query. The DataContext must implement IFutureContext. Then, when one of the IFutureQuery objects is enumerated, it calls back to IFutureContext.ExecuteFutureQueries() via the LoadAction delegate. ExecuteFutureQueries builds a batch query from all the stored IFutureQuery objects. Finally, all the IFutureQuery objects are updated with the results from the query.
Limitations
Does not support anonymous types.