Я могу выполнить соединение, используя свойство навигации, которое для меня более DRY, потому что я не повторяю критерии соединения везде:
(from c in db.Companies
from e in c.Employees
select new { Employee = e, Company = c}).ToList();
Поскольку ORM знает, как компании связаны с сотрудниками, c.Employees
навигации c.Employees
используется для определения критериев FK для соединения.
Что такое прямой перевод на расширение/лямбда-синтаксис множественного предложения из свойства навигации?
Я знаю, что существует метод расширения Объединения, но требует, чтобы вы явно указывали, что FK сравнивается, а не подразумевает, что критерии имеют свойство навигации. Это не работает, но, надеюсь, выражает мои намерения:
db.Companies
.Join(c.Employees, /* don't want to explicitly name FKs*/)
.Select(x => new { Employee = x.e, Company = x.c}).ToList();
Конечно, Join(c.Employees
не работает, потому что в этом контексте нет c
, но идея как-то использует свойство навигации Companies.Employees, чтобы подразумевать критерии соединения.
Я знаю, что могу сделать:
db.Companies.Select(c => new { Employees = c.Employees, Company = c })
но это другой набор результатов, поскольку он возвращает одну запись для каждой компании, а затем список сотрудников как вложенное свойство. Вместо первого, который является объединением, таким образом, есть запись для каждой связанной комбинации, и результат имеет свойство Employee
вместо коллекции Employees
.
Я не уверен, но думаю, что .SelectMany
- это прямой перевод. Вы не получите ссылку c
родительской, поэтому, если вы выполните несколько из них:
db.Companies.SelectMany(c=>c.Employees).SelectMany(e=>e.VacationDays).Select(v => new { VacationDay = v, Employee = v.Employee, Company = v.Employee.Company })
Вы должны идти назад через отношения, чтобы сгладить соединение. В linq это намного проще, потому что у вас есть c
, e
и v
все в контексте выбора. Я не знаю, можете ли вы выразить одно и то же в методах расширения, так что все три псевдонима/ссылки передаются вниз. Возможно, это просто следствие синтаксиса метода расширения, но надеясь, что кто-то предоставит лучший эквивалент.
SelectMany
это действительно то, что многократный from
пунктов отображаются в.
Чтобы поддерживать переменные в области охвата проекции, каждому SelectMany
необходимо проецировать последовательность в новый анонимный объект, который сохраняет все соответствующие переменные в области видимости:
var query = db.Companies.SelectMany(company => company.Employees,
(company, employee) => new
{
company,
employee
});
Чтобы добавить дополнительные прогнозы для дополнительных вложенных свойств навигации, просто повторите шаблон с последующим вызовом SelectMany
:
var query = db.Companies.SelectMany(company => company.Employees,
(company, employee) => new
{
company,
employee
}).SelectMany(pair => pair.employee.VacationDays,
(pair, vactionDay) => new
{
pair.company,
pair.employee,
vactionDay,
});
См. Это сообщение в блоге для более подробной информации и подробного описания этого преобразования и того, как он масштабируется.
Разве это не было бы чем-то вроде:
db.Employees.Select(m => new { Employee = m, Company = m.Company });
Поскольку у каждого сотрудника есть Компания, почему бы просто не добавить навигационное свойство "Компания" в компанию Employee?
Чтобы получить отпуск, просто измените его на следующее:
db.Employees.SelectMany(
employee => employee.VacationDays,
(employee, vacationDay) => new
{
Employee = employee,
Company = employee.Company,
VacationDay = vacationDay
});
Обновить:
На самом деле, нет никакой разницы между:
(from c in db.Companies
from e in c.Employees
select new { Employee = e, Company = c}).ToList();
а также:
(from e in c.Employees
from c in db.Companies
select new { Employee = e, Company = c}).ToList();
Employee
forCompany
. это то, что он использует. Он пытается использовать его, чтобы спроецировать свой запрос на коллекцию пар сотрудника и компании. Он делает это прекрасно, используя синтаксис запроса, но не делает этого, используя синтаксис метода. Вы не смоделировали этот синтаксический запрос в этом ответе.