SelectMany Three Levels Deep

Я могу сгладить результаты дочерней коллекции в коллекции с помощью SelectMany:

 // a list of Foos, a Foo contains a List of Bars
 var source = new List<Foo>() { ... };

 var q = source.SelectMany(foo => foo.Bar)
     .Select(bar => bar.barId)
 .ToList();

это дает мне список всех идентификаторов Bar в списке Foo. Когда я пытаюсь пройти три уровня, возвращается неправильный результат.

 var q = source.SelectMany(foo => foo.Bar)
     .SelectMany(bar => bar.Widget)
         .Select(widget => widget.WidgetId)
 .ToList();

Как я должен использовать SelectMany, чтобы получить список всех виджетов на всех барах в моем списке Foos?

Edit Я пропустил вышеупомянутое предложение, но код отражает цель. Я ищу список всех идентификаторов виджетов, а не виджеты.

"Неверный" результат - это не все идентификаторы виджетов.

Ответы

Ответ 1

Ваш запрос возвращает все идентификаторы виджетов, а не все виджеты. Если вам просто нужны виджеты, просто используйте:

var q = source.SelectMany(foo => foo.Bar)
              .SelectMany(bar => bar.Widget)
              .ToList();

Если это все еще дает "неправильный результат", пожалуйста, объясните, каким образом это неправильный результат. Пример кода будет очень полезен:)

EDIT: Хорошо, если вам нужны идентификаторы виджетов, ваш исходный код должен быть в порядке:

var q = source.SelectMany(foo => foo.Bar)
              .SelectMany(bar => bar.Widget)
              .Select(widget => widget.WidgetId)
              .ToList();

Это также можно записать как

var q = (from foo in source
         from bar in foo.Bar
         from widget in bar.Widget
         select widgetId).ToList();

если вам нравится формат выражения запроса.

Это действительно должно сработать - если оно не работает, это говорит о том, что что-то не так с вашими данными.

Мы должны были проверить раньше - это просто LINQ to Objects или поставщик фанов (например, LINQ to SQL)?

Ответ 2

var q = (
    from f in foo
    from b in f.Bars
    from w in b.Widgets
    select w.WidgetId
   ).ToList();

Также обратите внимание, что если вам нужен уникальный список, вы можете сделать .Distinct(). ToList().

Ответ 3

       var q = source.SelectMany(foo => foo.Bar)
          .SelectMany(bar => bar.Widget,(bar,widget) => widget.WidgetId)
          .ToList();

мы можем вызвать эту перегрузку SelectMany(), чтобы мы могли указать проекцию, используя лямбда-эксперимент