Как скопировать данные из объекта Sql Object в С# Model

У меня есть две таблицы:

  • Сотрудник: Id, Name, DepartmentId
  • Отдел: Id, Name

Employee.cs:

public int Id {get;set;}
public string Name {get;set;}
public int DepartmentId {get;set;}

Department.cs:

public int Id {get;set;}
public string Name {get;set;}

ViewModel: EmployeeDepartmentVM:

public Department department {get;set;}
public List<Employee> employees {get;set;}

Чтобы присоединиться к этим двум таблицам, я написал этот код:

    SELECT E.* , D.Id as DId , D.Name as DName 
     from [Employee] as E
     LEFT OUTER JOIN [Department] as D
     ON E.DepartmentId = D.Id
     where D.Id = 1

Как мне получить тип EmployeeDepartmentVM из указанного выше запроса?

Я знаю, если я напишу модель, как моя проблема будет решена:

public int Id {get;set;}
public string Name {get;set;}
public int DepartmentId {get;set;}
public int DId {get;set;}
public string Name {get;set;}

Но я не хочу писать дополнительную модель. Просто попробуйте данные запроса привязки в тип EmployeeDepartmentVM.

Ответ 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();