Ответы
Does p refer to products after into keyword?
p в предложении from
- это новая локальная переменная, относящаяся к одному продукту одной категории.
Is ps the group of product objects? I mean a sequence of sequences.
Да, ps
- группа товаров для категории c
. Но это не последовательность последовательностей, просто IEnumerable<Product>
, точно так же, как c
- это отдельная категория, а не все категории в объединении групп.
В запросе вы видите данные только для одного результата строка , никогда не будет результатом объединения всей группы. Посмотрите на окончательный select
, он печатает одну категорию и один продукт, к которому он присоединился. Этот продукт относится к группе продуктов ps
, к которой присоединилась категория one .
Затем запрос выполняет обход всех категорий и всех групп продуктов.
If DefaultIfEmpty() isn't used, doesn't p, from p in ps.DefaultIfEmpty(), run into select? Why?
Это не равно Select
, потому что предложение from
создает новое соединение с самим собой, которое превращается в SelectMany
.
Структура
Принимая запрос по частям, сначала присоединяется группа:
from c in categories
join p in products on c equals p.Category into ps
После этого можно использовать только c
и ps
, представляющие категорию и связанные с ней продукты.
Теперь обратите внимание, что запрос целом имеет ту же форму, что и:
from car in Cars
from passenger in car.Passengers
select (car, passenger)
, который объединяет Cars
со своим собственным Passengers
, используя Cars.SelectMany(car => car.Passengers, (car, passenger) => (car, passenger));
Так в вашем запросе
from group_join_result into ps
from p in ps.DefaultIfEmpty()
создает новое объединение предыдущего результата объединения групп со своими собственными данными (списками сгруппированных продуктов), прошедшими через DefaultIfEmpty с использованием SelectMany.
Заключение
В конце концов, сложность заключается в запросе Linq, а не в методе DefaultIfEmpty. Этот метод просто объясняется на странице MSDN, которую я разместил в комментарии. Он просто превращает коллекцию без элементов в коллекцию, которая имеет 1 элемент, который является либо значением по умолчанию (), либо предоставленным значением.
Скомпилированный источник
Это примерно C# код запрос компилируется в:
//Pairs of: (category, the products that joined with the category)
IEnumerable<(string category, IEnumerable<Product> groupedProducts)> groupJoinData = Enumerable.GroupJoin(
categories,
products,
(string c) => c,
(Product p) => p.Category,
(string c, IEnumerable<Product> ps) => (c, ps)
);
//Flattening of the pair collection, calling DefaultIfEmpty on each joined group of products
IEnumerable<(string Category, string ProductName)> q = groupJoinData.SelectMany(
catProdsPair => catProdsPair.groupedProducts.DefaultIfEmpty(),
(catProdsPair, p) => (catProdsPair.category, (p == null) ? "(No products)" : p.ProductName)
);
Выполнено с помощью ILSpy с использованием C# 8.0 view.