VB.NET Returns IEnumerable(Of IEnumerable(Of T)) when Using Group By LINQ Statement

Viewed 1785

I am trying to convert Anthyme Caillard's INotifyDataErrorInfo implementation into VB.NET. All goes well until I reach the Validate method, which has the following LINQ query, in which q is of type IEnumerable<IGrouping<string, ValidationResult>>:

var q = from r in validationResults
        from m in r.MemberNames
        group r by m into g
        select g;

I have tried the following translation:

Dim q = From r In valResults
        From m In r.MemberNames
        Group r By r.MemberNames Into Group
        Select Group

Or even the lambda expression version, (assuming I'm using John Skeet's syntax correctly):

Dim q = valResults.GroupBy(Function(r) r, Function(r) r.MemberNames)

but in both of these, q is of type IEnumerable(Of IEnumerable(Of ValidationResult)).

I have looked at VB and IGrouping for LINQ Query and VB.Net - Linq Group By returns IEnumerable(Of Anonymous Type), but I don't think they apply since I will be working with the group directly, and not a specialized class.

Why aren't these implementations returning the same thing, and what can I do to make q the required IEnumerable(Of IGrouping(Of String, ValidationResult))?

1 Answers

It seems that the VB compiler treats LINQ query syntax different than the C# compiler.

In C# the expression

from element in list
group element by element.Value

is identical to

list.GroupBy(element => element.Value)

In VB however

From element In list
Group element By element.Value Into g = Group

is translated into

list.GroupBy(Function(element) element.Value,
             Function(key, element) New With { Key .Value = key, Key .g = element })

I can't see any good reason why the VB compiler introduces an anonymous type here - but that's what it does.

I recommend you always translate C# LINQ query syntax (from a in list select a.Value) into a method chain (list.Select(a => a.Value)). That way the VB compiler can't interfere. It is forced to use the exact chain of methods.

However your translation of the original query isn't correct.

var q = from r in validationResults
        from m in r.MemberNames
        group r by m into g
        select g;

is actually translated into

var q = validationResults.SelectMany(r => r.MemberNames, (r, m) => new { r, m })
                         .GroupBy(t => t.m, t => t.r);

which in VB becomes:

Dim q = validationResults.SelectMany(Function(r) r.MemberNames,
                                     Function(r, m) New With { Key .r = r, Key .m = m }) _
                         .GroupBy(Function(t) t.m, Function(t) t.r)
Related