entity-framework - 如何使用实体框架编辑嵌套类的此 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 查询以获得上述结果?
谢谢你的帮助。
解决方案
您将直接从查询中返回数据库模型实体类型 - 所以当然您将获得集合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 和实体类代表不同的事物,因此您不应该使用相同的类型来表示它们。幸运的是,有一些工具可以自动完成大部分工作,例如AutoMapper
VS 扩展和 T4 脚本,它们将生成“脚手架”DTO 和其他类,其中已经为您生成了重复部分。
资源:
- 自动映射器:https ://automapper.org/
- 使用 Roslyn 的 DTO 脚手架:https ://marketplace.visualstudio.com/items?itemName=Dtogenerator.DTOGenerator
- 来自 Microsoft 的一篇关于 DTO 用途的文章。
- 这篇文章(和我的回答)都演示了具有可变属性的 DTO,因为 C# 通过其对象初始化语法可以轻松地使用可变对象属性,但是理想情况下 DTO 应该是 immutable,而 C# 还不能使其易于使用(C# 9 在对象初始化器中引入了对不可变属性的支持,哇——但我不同意其中的一些细节,例如引入新关键字)
推荐阅读
- spartacus-storefront - 替换现有 ProductListComponent 时激活路由的问题
- javascript - V8 中的垃圾收集器在分配给新对象(浅拷贝)时如何处理 Promise?
- ray - 在前台的 docker 容器中运行 ray 服务,而不是守护程序模式
- azure-blob-storage - 503 - 从 .net core 3.1 Web API 重定向到 Azure Blob Url 时服务不可用
- python - 从枚举具有重复值的函数返回枚举
- react-native - 如何为 React Native 的 TextInput 中的部分文本应用样式?
- ruby - 如何使用 (**) 运算符而不是 pow 实现相同的功能
- jquery - 更改数据表上的文本颜色重新加载
- google-apps-script - Google 脚本 - 无法读取未定义的属性“范围”(第 30 行,文件“代码”)
- swiftui - 如果操作未立即完成,则 SwiftUI 视图在绑定到 @Published var 时没有动画