LINQ: Immutability vs. Deferred execution
The last couple of nights I've been playing with some Linq to Sql and a whole lot of Linq to Objects and I have to say where coming up with complex Regular Expressions used to be one of my favorite puzzles, coming up with complex projections and transformations through Linq is quickly taking its place. Simple Linq is well documented, but when it comes to aggregation, it's a lot sparser. I expect to write more of that up once I feel more comfortable with the syntax.In the meantime, I wanted to write up some non-obvious observation about deferred execution with Linq. Considering the gotchas with lambdas, it's easy to extend the lessons learned to linq, since it is after all deferred execution. But what's different with Linq is that, while execution is deferred, the expression tree built via a query is also immutable. I came across this trying to do some simple query re-use.
Let's start with a simple DTO:
public class Order { public Order(int id, int val, bool buyOrder) { Id = id; Value = val; IsBuyOrder = buyOrder; } public int Id { get; set; } public int Value { get; set; } public bool IsBuyOrder { get; set; } }
Order[] orders = new Order[] { new Order(1,2,true), new Order(2,2,false), new Order(3,4,true), new Order(4,4,false), new Order(5,6,true), new Order(6,6,false), };
Let's split those into buy and sell orders:
var buyOrders = from order in orders where order.IsBuyOrder select order; var sellOrders = from order in orders where !order.IsBuyOrder select order;
If we want to find the buy and the sell order with a value of 2, you'd think we could write one query and re-use it for both of those queries. Since both queries results in IEnumerable<Order>, how about we define a query source and assign the value of either above query.
IEnumerable<Order> orders2 = null; var orderAtTwo = from order in orders2 where order.Value == 2 select order; orders2 = buyOrders; int buyOrderId = orderAtTwo.First().Id; orders2 = sellOrders; int sellOrderId = orderAtTwo.First().Id; Console.WriteLine("buy Id: {0}, sell Id: {1}", buyOrderId, sellOrderId);
A way around this is to replace the actual contents of orders2. However, for us to do that, we have to turn it into the query source into a collection first.
orders2.Clear(); orders2.AddRange(buyOrders); int buyOrderId = orderAtTwo.First().Id; orders2.Clear(); orders2.AddRange(sellOrders); int sellOrderId = orderAtTwo.First().Id; Console.WriteLine("buy Id: {0}, sell Id: {1}", buyOrderId, sellOrderId);
buy Id: 1, sell Id: 2
To achieve this, we need to move the shared query into a separate method such as:
private IEnumerable<Order> GetTwo(IEnumerable<Order> source) { return from order in source where order.Value == 2 select order; }
int buyOrderId = GetTwo(buyOrders).First().Id; int sellOrderId = GetTwo(sellOrders).First().Id; Console.WriteLine("buy Id: {0}, sell Id: {1}", buyOrderId, sellOrderId);

0 Comments:
Post a Comment
<< Home