|
Continuous LINQ, if you haven't already heard, is a project that I started and put up on CodePlex to address the issue of static LINQ queries. Without something like CLINQ, every LINQ query you write is static. Once GetEnumerator() is invoked on the query expression, it is no longer associated with the collection that was the source of the query. This means that if you query for a list of a customer's orders from the OrderMaster collection at 10:00am, then your results query will be completely unaware of any new orders added to the order master anytime after 10:00am. People usually get around this by periodically rebuilding the result set in the background on a timer or some other kind of polling thread. I hate background polling threads, which was one of my primary motivations in writing CLINQ.
Now that CLINQ has some road time, I've decided to put a couple more features in it. My favorite, which is going to be in the upcoming 1.1.0.0 release (I'll be releasing it once I write some more up to date demos for the demo suite), is the notion of Continuous Aggregation. Currently, CLINQ will auto-update your collection as the result of a CLINQ query, but that's all it can do - you write a query against a collection and get a collection back. What if you want to aggregate a CLINQ results query (or any other observable collection, for that matter) into a single scalar value such as a Sum or an Average?
Currently, using v1.0 of CLINQ, the call to any of the aggregation functions will detach, or "discontinue" if you will, the connected result set. So you'll get the scalar quantity you expect, but while the CLINQ results are still updating dynamically, your scalar quantity is sitting there stagnant and you are faced with the grim possibility of having to write a background poller to update the aggregate value.
Now with v1.1, you can write code that creates continuous aggregates, as shown in some of the samples below:
ContinuousValue<int> maxAge = allCustomers.ContinuousMax<Customer>(c => c.Age);
Note that we're passing a lambda function into the continuous max method that acts as a selector. It is basically a transform that is applied to every element in the collection that converts the element into an int. In this case, I want the age of each element to be used for comparison in the Max method that is already supplied by .NET.
The reason that the lambda function is so beautiful is that you can do whatever you like. What if I want to return the sum of the cost of a bunch of order items, but I don't want the tax included and I don't have a single property for that? I can use the lambda function to strip out the tax:
ContinuousValue<decimal> totalPrice = orderMaster.ContinuousSum<Order>(
lineItem => lineItem.TotalCost - lineItem.Tax);
Now we're looking at being able to do things declaratively so that they are easy to maintain, easy to read, and easy to upgrade that would otherwise require the creation of methods that might be nowhere near self-documenting.
What if we want to return the average total cost of an order, but our order object doesn't have a roll-up property on it? We can actually pass a continuous aggregation around inside the selector lambda for yet another continuous aggregation:
ContinuousValue<decimal> ordersTotalCost = allCustomers.ContinuousSum<Customer>(
customer => customer.Orders.ContinuousSum<Order>( o=> o.ItemPrice * o.ItemQuantity));
The inner continous sum for each customer order will remain continous even after this query executes. By that I mean that once I define this query, I can not only add new customers to the collection, but I can add order items to individual customers, and the updates will propogate back along the monitor chain all the way back to my aggregate value.
The only thing left to do is bind a WPF GUI to the continuously updating aggregate scalar value:
myControl.DataContext = ordersTotalCost.CurrentValue;
That's it!! Simple enough? I'm not done with the entire implementation, but the following is a list of the aggregate functions I currently have coded and tested:
If you have ideas for continuous aggregates that you think I'm missing, feel free to drop by the CodePlex site for CLINQ and start up a discussion topic about it.
As usual, I will keep posting updates here and on the CLINQ CodePlex site especially when I get around to compiling the release 1.1.0.0 build.