This is the second in a series of articles about bugs I encounter while working. The first one was about GETUTCDATE()
and how it complicates things in longer queries.
Today’s bug is about LINQ and the lazy IEnumerables
it produces. A one-off script on which I spent way too much time worked something like this:
IEnumerable<Item> items = RawItems().Select(i => i.Transform());
foreach (var item in items)
item.name = $"{item.name} #Modified";
UpdateItems(items);
Granted, not the most elegant code. But I thought it was enough to do it’s job. Turns out there’s a simple yet subtle bug occurring. Turns out that Select
is quite lazy, so items
is more like a promise that if you’re iterating it you’ll get the result of Transform()
applied to each element from RawItems
. But every time the iterator is used, that computation gets redone.
So the foreach
loop actually modified each resulting element from one invocation of the iterator, but all that data was thrown away. When UpdateItems
used items
it obtained a new version of the derived data, without any of the modifications.
The solution is simple. Either include the modification as part of the Select
and alongside Transform
. Or make items
non-lazy, by turning it into an List
or Array
, which is what I ended up doing for simplicity’s sake in my actual setup. So the code was now just:
IEnumerable<Item> items = RawItems().Select(i => i.Transform()).ToList();
foreach (var item in items)
item.name = $"{item.name} #Modified";
UpdateItems(items);
Since the foreach
loop and UpdateItems
now referenced a list, instead of the iterator over something, the desired behaviour was achieved.
Top comments (0)