Ответ 1
Я действительно не понимаю, в чем проблема. Определение EmployeeDepartmentVM
означает, что вам нужно сгруппировать результирующий набор с помощью Department
. Предполагая, что результирующий набор неупорядочен, его можно достичь, просто сохранив словарь для поиска моделей просмотра уже добавленных отделов во время чтения.
Что приводит к чему-то вроде этого:
static List<EmployeeDepartmentVM> GetEmployeeDepartmentVMList(DbCommand command)
{
var resultById = new Dictionary<int, EmployeeDepartmentVM>();
using (var reader = command.ExecuteReader())
{
var employeeIdCol = reader.GetOrdinal("Id");
var employeeNameCol = reader.GetOrdinal("Name");
var departmentIdCol = reader.GetOrdinal("DId");
var departmentNameCol = reader.GetOrdinal("DName");
while (reader.Read())
{
var departmentId = reader.GetInt32(departmentIdCol);
EmployeeDepartmentVM result;
if (!resultById.TryGetValue(departmentId, out result))
{
result = new EmployeeDepartmentVM
{
department = new Department(),
employees = new List<Employee>()
};
result.department.Id = departmentId;
result.department.Name = reader.GetString(departmentNameCol);
resultById.Add(departmentId, result);
}
var employee = new Employee();
employee.Id = reader.GetInt32(employeeIdCol);
employee.Name = reader.GetString(employeeNameCol);
employee.DepartmentId = departmentId;
result.employees.Add(employee);
}
}
return resultById.Values.ToList();
}
Некоторые примечания. Как написано, ваш SQL-запрос подразумевает, что поля, связанные с отделом, могут быть пустыми (LEFT OUTER JOIN
). Однако предложение WHERE
, а также модель Employee (поле DepartmentId
, не допускающее nullable) означает, что этого не может быть. Если намерение состоит в том, чтобы включить отделы без сотрудников, то лучше изменить соединение на RIGHT OUTER
и использовать что-то вроде этого:
// ...
if (reader.IsDBNull(employeeIdCol)) continue;
var employee = new Employee();
// ...
EDIT: Для полноты, вот еще один подход. Это похоже на то, как EF материализует подобные запросы и не нуждается в временном словаре, но требует, чтобы набор ввода был заказан PK главной таблицы, поэтому вам нужно добавить
ORDER BY D.Id
в конце вашего SQL. Базы данных могут легко и эффективно предоставлять такой порядок, и преимущество этого решения в том, что оно позволяет отсрочить выполнение и не требует обработки всего набора, чтобы начать возвращать результаты. Это не важно, если вы хотите просто получить список, но можете быть полезны в других сценариях.
static IEnumerable<EmployeeDepartmentVM> GetEmployeeDepartmentVMs(DbCommand command)
{
using (var reader = command.ExecuteReader())
{
var employeeIdCol = reader.GetOrdinal("Id");
var employeeNameCol = reader.GetOrdinal("Name");
var departmentIdCol = reader.GetOrdinal("DId");
var departmentNameCol = reader.GetOrdinal("DName");
for (bool more = reader.Read(); more;)
{
var result = new EmployeeDepartmentVM
{
department = new Department(),
employees = new List<Employee>()
};
result.department.Id = reader.GetInt32(departmentIdCol);
result.department.Name = reader.GetString(departmentNameCol);
do
{
if (reader.IsDBNull(employeeIdCol)) continue;
var employee = new Employee();
employee.Id = reader.GetInt32(employeeIdCol);
employee.Name = reader.GetString(employeeNameCol);
employee.DepartmentId = result.department.Id;
result.employees.Add(employee);
}
while ((more = reader.Read()) && reader.GetInt32(departmentIdCol) == result.department.Id);
Debug.Assert(!more || reader.GetInt32(departmentIdCol) > result.department.Id); // Sanity check
yield return result;
}
}
}
Чтобы получить список, как в первом подходе, просто добавьте ToList()
после вызова, например.
var result = GetEmployeeDepartmentVMs(command).ToList();