如何将数据从Sql对象复制到C#模型属性

时间:2016-05-13 08:34:04

标签: c# sql sql-server ado.net ado.net-entity-data-model

我有两张桌子:

  • 员工:IdNameDepartmentId
  • 部门:IdName

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 个答案:

答案 0 :(得分:7)

我真的不明白什么是挑战。 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查询意味着Department相关字段可以为null(LEFT OUTER JOIN)。但是,WHERE子句以及Employee模型(DepartmentId字段不可为空)意味着它不会发生。如果意图包括没有员工的部门,那么最好将联接更改为RIGHT OUTER并使用以下内容:

// ...
if (reader.IsDBNull(employeeIdCol)) continue;
var employee = new Employee();
// ...  

编辑:为了完整起见,这是另一种方法。它与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();