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

How to Copy Data From Sql Object to C# Model Property

我有两个表:

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;}

视图模型: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 类型。

我真的看不出有什么挑战。 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 字段不可为空)暗示它不可能发生。如果打算包括没有员工的部门,那么最好将联接更改为 RIGHT OUTER 并使用如下内容:

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

编辑: 为了完整起见,这是另一种方法。类似于EF物化类似查询的方式,不需要临时字典,但是需要输入集按master的PK排序table,所以需要加上

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();