Fabian's Mix

Mixins, .NET, and more

re-linq: Support for SelectMany without result selector

without comments

While this has been missing from the start, different people reported it at about the same time a few weeks ago: support for the SelectMany overload without a result selector.

What does that mean?

Consider the following C# LINQ query:

from c in Cooks

from k in Kitchens

select c.Name + ” at “ + k.Name

This is equivalent to this query, which makes the query operators being used explicit:

Cooks.SelectMany (c => Kitchens, (c, k) => c.Name + ” at “ + k.Name)

SelectMany is a query operator that combines each item from an input sequence with each item from a second input sequence. The latter is calculated by means of a collection selector – the first argument to the SelectMany method in the example above. The collection selector returns the Kitchens sequence for each call; but it could potentially return a separate sequence for each c.

So, the purpose of SelectMany is to combine items from the sequences. But how does it combine them? This is where the second argument comes into play: the result selector. In the example, it builds a combined string from the cook’s and kitchen’s names.

Okay, now this is LINQ 101, where is the point?

There are cases where you don’t really want to combine the items of two sequences; you just want to “flatten” a nested sequence. Here’s an example:

var allAssistants = Cooks.SelectMany (c => c.Assistants);

This code simply takes all assistants of all cooks and returns them as a flattened sequence.

While the SelectMany overload is convenient, it will never be used when you write a LINQ query using the C# syntactic sugar form (from … select …). That’s why re-linq hasn’t included any default support for it – until now. Starting with build 1.13.88 (released today), re-linq can now parse this query out of the box.

About the technical details: when encountering a query using the simpler overload, such as the one above, re-linq just pretends there is a trivial result selector:

var allAssistants = Cooks.SelectMany (c => c.Assistants, (c, a) => a);

That way, the parsing code didn’t need to be changed at all. The QueryModel also doesn’t care about which overload you used – it will always represent SelectMany as an AdditionalFromClause. This means that LINQ providers using re-linq don’t have to change anything to make this work.

Which is certainly a good thing.

Written by Fabian

December 23rd, 2010 at 3:27 pm

Posted in re-linq

Leave a Reply