【发布时间】:2021-05-05 11:45:47
【问题描述】:
我的项目中有一个自引用实体Comment:
public class Comment
{
public Guid CommentId { get; set; }
public string Content { get; set; }
public Guid? ParentCommentId { get; set; }
public virtual Comment ParentComment { get; set; }
public virtual ICollection<Comment> Children { get; set; }
}
我正在尝试执行以下操作:
await context.Comment
.Where(c => c.ParentCommentId == null)
.Include(c => c.Children)
.ToListAsync();
我想获取所有根 cmets(没有父级的 cmets)并加载 cmets 的整个层次结构。
我想在结果中看到吗?
为了使其更具可读性,我按层次顺序表示它(仅Content 属性):
- 世界你好!
- 您是程序员吗?
- 当然
- 什么?
- 您是程序员吗?
- 我也想去火星!
- 月球见 :)
执行查询时,如上所示,我想要得到类似这样的东西(在 JSON 中):
[
{
"commentId":"be02742a-9170-4335-afe7-3c7c22684424",
"content":"Hello World!",
"parentCommentId":null,
"children":[
{
"commentId":"59656765-d1ed-4648-8696-7d576ab7419f",
"content":"Are you a programmer?",
"parentCommentId":"be02742a-9170-4335-afe7-3c7c22684424",
"children":[
{
"commentId":"0bb77a43-c7bb-482f-9bf8-55c4050974da",
"content":"Sure",
"parentCommentId":"59656765-d1ed-4648-8696-7d576ab7419f",
"children":[
]
},
{
"commentId":"b8d61cfd-d274-4dae-a2be-72e08cfa9066",
"content":"What?",
"parentCommentId":"59656765-d1ed-4648-8696-7d576ab7419f",
"children":[
]
}
]
}
]
},
{
"commentId":"cfe126b3-4601-4432-8c87-445c1362a225",
"content":"I wanna go to Mars too!",
"parentCommentId":null,
"children":[
{
"commentId":"ab6d6b49-d772-48cd-9477-8d40f133c37a",
"content":"See you on the Moon :)",
"parentCommentId":"cfe126b3-4601-4432-8c87-445c1362a225",
"children":[
]
}
]
}
]
但是我得到了什么?
当我执行这个查询时,我得到以下结果:
[
{
"commentId":"be02742a-9170-4335-afe7-3c7c22684424",
"content":"Hello World!",
"postId":"69f3ca3a-66fc-4142-873d-01e950d83adf",
"post":null,
"parentCommentId":null,
"parentComment":null,
"commentRates":[
],
"inverseParentComment":[
{
"commentId":"59656765-d1ed-4648-8696-7d576ab7419f",
"content":"Are you a programmer?",
"postId":"69f3ca3a-66fc-4142-873d-01e950d83adf",
"post":null,
"parentCommentId":"be02742a-9170-4335-afe7-3c7c22684424",
"commentRates":[
],
"inverseParentComment":[
]
}
]
},
{
"commentId":"cfe126b3-4601-4432-8c87-445c1362a225",
"content":"I wanna go to Mars too!",
"postId":"69f3ca3a-66fc-4142-873d-01e950d83adf",
"post":null,
"parentCommentId":null,
"parentComment":null,
"commentRates":[
],
"inverseParentComment":[
{
"commentId":"ab6d6b49-d772-48cd-9477-8d40f133c37a",
"content":"See you on the Moon :)",
"postId":"69f3ca3a-66fc-4142-873d-01e950d83adf",
"post":null,
"parentCommentId":"cfe126b3-4601-4432-8c87-445c1362a225",
"commentRates":[
],
"inverseParentComment":[
]
}
]
}
]
所以,我得到以下层次结构:
- 世界你好!
- 您是程序员吗?
- 我也想去火星!
- 月球见 :)
改为:
- 世界你好!
- 您是程序员吗?
- 当然
- 什么?
- 您是程序员吗?
- 我也想去火星!
- 月球见 :)
为什么会这样?我该如何解决这个问题?
工作,但肮脏的解决方案
List<Comment> allComments = await context.Comment
.Include(c => c.Children)
.ToListAsync();
List<Comment> filteredComments = allComments.Where(c => c.ParentCommentId == null);
这可行,但很丑 - 我们将 cmets 的完整层次结构从数据库加载到内存中,然后过滤它们。如果数据库包含例如 100 万个 cmets,它的工作速度会非常慢。
更新
就我而言,不需要使用递归 CTE。请参阅this question 的更新。它看起来像 Greg Ogle 介绍的想法,但更简化了。
【问题讨论】:
-
EF Core 不支持递归 CTE。如果您真的关心性能,请通过 SQL 执行此操作。
-
采用更强大的 ORM linq2db。你可以在里面使用CTE。
-
@SvyatoslavDanyliv,谢谢。我认为这是个好主意。
标签: c# entity-framework-core hierarchy self-referencing-table