首页 > 解决方案 > 如何使用实体框架编辑嵌套类的此 LINQ 查询

问题描述

我有以下课程:

public class Employee 
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Gender { get; set; }
    public DateTime JoinedCompany { get; set; }
    public ICollection<EmployeeSkill> EmployeesSkills { get; set; }
}

public class Skill 
{
    public int Id { get; set; }
    public string SkillName { get; set; }
}

public class EmployeeSkill 
{
    public int EmployeeId { get; set; }
    public Employee Employee { get; set; }

    public int SkillId { get; set; }
    public Skill Skill { get; set; }
}

我也有一DbContext堂课:

public DbSet<Employee> Employees { get; set; }
public DbSet<Skill> Skills { get; set; }
public DbSet<EmployeesSkill> EmployeeSkills { get; set; }

现在,我可以通过以下方式取回数据:

public async Task<IReadOnlyList<Employee>> GetEmployeesAsync () 
{
    return await _context.Employees
                         .Include (p => p.EmployeeSkills)
                         .Include (p => p.Skill)
                         .ToListAsync ();
}

它返回如下内容:

[
  {
    "firstName": "Olivia",
    "lastName": "Lu",
    "gender": "F",
    "joinedCompany": "2015-03-31T00:00:00",
    "employeeSkills": [
      {
        "employeeId": 8,
        "skillId": 3,
        "skill": {
          "skillName": "Hadoop",
          "id": 3
        },
        "id": 1
      },
      {
        "employeeId": 8,
        "skillId": 4,
        "skill": {
          "skillName": "Spark",
          "id": 4
        },
        "id": 2
      }
    ],
    "id": 8
  }
]

使用 LINQ 时我真的很困惑,我永远无法找到正确的解决方案。我试图结束以下内容:

[
  {
    "firstName": "Olivia",
    "lastName": "Lu",
    "gender": "F",
    "joinedCompany": "2015-03-31T00:00:00",
    "employeeSkills": [
      {
        "skillId": 3,
        "skillName": "Hadoop"
      },
      {
        "skillId": 4,
        "skillName": "Spark"
      }
    ],
    "id": 8
  }
]

如何更改 LINQ 查询以获得上述结果?

谢谢你的帮助。

标签: entity-frameworklinqclassentity-framework-core

解决方案


您将直接从查询中返回数据库模型实体类型 - 所以当然您将获得集合EmployeeSkill中的完整结构employeeSkills

您需要定义一个新的 DTO 类型,该类型表示 an 的简洁表示,EmployeeSkill并在您已经加载数据后在您的应用程序代码中实现它(您可以使用匿名类型直接在 Linq 查询中执行此操作,但您不能导出/expose 匿名类型(我认为 EF Core 还不支持值元组)所以定义一个适当的DTO 类型以避免未来的痛苦。

无论如何,这样做:

class EmployeeDto
{
    [JsonProperty( "employeeId " )]
    public int      EmployeeId    { get; set; }
    [JsonProperty( "firstName" )]
    public string   FirstName     { get; set; }
    [JsonProperty( "lastName" )]
    public string   LastName      { get; set; }
    [JsonProperty( "gender" )]
    public string   Gender        { get; set; }
    [JsonProperty( "joinedCompany" )]
    public DateTime JoinedCompany { get; set; }

    [JsonProperty( "employeeSkills" )]
    public List<EmployeeSkillDto> EmployeesSkills { get; set; }
}

class EmployeeSkillDto
{
    [JsonProperty( "skillId" )]
    public Int32 SkillId { get; set; }

    [JsonProperty( "skillName" )]
    public String SkillName { get; set; }
}

public async Task<IReadOnlyList<EmployeeDto>> GetEmployeesAsync( CancellationToken cancellationToken = default ) {

    List<Employee> list = await _context.Employees
        .Include( p => p.EmployeeSkills )
        .Include( p => p.Skill )
        .ToListAsync( cancellationToken );

    // This part below is not async because it's done in-memory:
    List<EmployeeDto> dtoList = list
        .Select( e => new EmployeeDto()
        {
            EmployeeId = e.EmployeeId,
            FirstName = e.FirstName,
            LastName = e.LastName,
            /* etc */
            EmployeeSkills = e.EmployeeSkills
                .Select( s => new EmployeeSkillsDto()
                {
                    SkillId   = s.Skill.Id,
                    SkillName = s.Skill.Name
                } )
                .ToList()
        } )
        .ToList();

    return dtoList;
}

这可能看起来很枯燥,不必要的重复(我同意它!)但这是必要的,因为 DTO 和实体类代表不同的事物,因此您不应该使用相同的类型来表示它们。幸运的是,有一些工具可以自动完成大部分工作,例如AutoMapperVS 扩展和 T4 脚本,它们将生成“脚手架”DTO 和其他类,其中已经为您生成了重复部分。

资源:


推荐阅读