如何使用实体框架为嵌套类编辑此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查询以获得如上所述的结果?

谢谢你的帮助。

您将直接从查询中返回数据库模型实体类型,因此您当然将在employeeSkills集合中获得EmployeeSkill的完整结构。

您需要定义一个新的DTO类型,该类型表示EmployeeSkill的简洁表示,并在加载数据后在应用程序代码中具体化它(您可以使用匿名类型在Linq查询中直接执行此操作,但不能导出/公开匿名类型(我认为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和实体类代表不同的东西,所以不应该使用相同的类型来表示它们。幸运的是,有一些工具可以自动化其中的大部分,例如AutoMapper和VS扩展以及T4脚本脚手架";DTO和其他已经为您生成了重复部分的类。

资源:

  • 自动映射器:https://automapper.org/
  • 使用Roslyn的DTO脚手架工:https://marketplace.visualstudio.com/items?itemName=Dtogenerator.DTOGenerator
  • 一篇来自微软的关于DTO目的的文章。
    • 这篇文章(和我的回答(都展示了具有可变属性的DTO,因为C#通过其对象初始化语法使处理可变对象属性变得容易,然而,理想情况下DTO应该是不可变的,C#还没有让它变得容易使用(C#9在对象初始化器中引入了对不可变属性的支持,woo-但我不同意其中的一些细节,比如引入一个新关键字(

最新更新