C# EF Core+Automapper ProjectTo中的递归CTE
在我的项目中,我有Comment和CommentDto类型: 我有以下递归CTE,封装在表值函数中: 此函数允许获取给定帖子的注释层次结构,它采用帖子的ID 数据库中存储有注释: 为了使其更具可读性,我将其表示为仅按层次顺序排列的内容属性: 你好,世界! 你是程序员吗? 当然 什么 我也想去火星! 月球上见: 使用efcore调用函数fn_后共产主义 注意:我做了粗体注释,没有父根注释 将注释映射到CommentDto 上面的代码使用实体类型Comment,但我想将其映射到CommentDto。因此,让我们使用ProjectTo实现此目的:C# EF Core+Automapper ProjectTo中的递归CTE,c#,sql,entity-framework-core,automapper,recursive-cte,C#,Sql,Entity Framework Core,Automapper,Recursive Cte,在我的项目中,我有Comment和CommentDto类型: 我有以下递归CTE,封装在表值函数中: 此函数允许获取给定帖子的注释层次结构,它采用帖子的ID 数据库中存储有注释: 为了使其更具可读性,我将其表示为仅按层次顺序排列的内容属性: 你好,世界! 你是程序员吗? 当然 什么 我也想去火星! 月球上见: 使用efcore调用函数fn_后共产主义 注意:我做了粗体注释,没有父根注释 将注释映射到CommentDto 上面的代码使用实体类型Comment,但我想将其映射到CommentDto
List<CommentDto> commentHierarchy = await _context.Comment
.FromSqlInterpolated($"SELECT CommentId, Content, PostId, ParentCommentId FROM dbo.fn_PostCommentHierarchy('post-id-here')")
.ProjectTo<CommentDto>(_mapper.ConfigurationProvider)
.ToListAsync();
注意:我做了粗体注释,没有父根注释。
比较使用ProjectTo之前和之后的结果。为什么它们不同
对于上述代码,EF Core将以下SQL查询发送到SQL server:
SELECT [c].[CommentId], [c].[Content], [c].[ParentCommentId], [c0].[CommentId], [c0].[Content], [c0].[ParentCommentId]
FROM (
SELECT CommentId, Content, PostId, ParentCommentId FROM dbo.fn_PostCommentHierarchy('69f3ca3a-66fc-4142-873d-01e950d83adf')
) AS [c]
LEFT JOIN [Comment] AS [c0] ON [c].[CommentId] = [c0].[ParentCommentId]
ORDER BY [c].[CommentId], [c0].[CommentId]
问题
为什么使用ProjectTo之前的结果和使用ProjectTo之后的结果不一样?
如何解决这个问题
更新1
根据Svyatoslav Danyliv的说法:
递归CTE返回平面列表,然后必须构建层次结构 再说一遍 但是为什么在这种情况下我应该使用递归CTE呢? 以下解决方案的工作方式相同:
List<CommentDto> commentFlatList = await _context.Comment
.Where(c => c.PostId == Guid.Parse("post-id-here"))
.ProjectTo<CommentDto>(_mapper.ConfigurationProvider)
.ToListAsync();
Dictionary<Guid, CommentDto> commentDictionary = commentFlatList
.ToDictionary(c => c.CommentId);
foreach (var comment in commentFlatList)
{
if (comment.ParentCommentId == null)
{
continue;
}
if (commentDictionary.TryGetValue((Guid) comment.ParentCommentId, out CommentDto parent))
{
parent.Children.Add(comment);
}
}
List<CommentDto> commentHierarchy = commentFlatList.Where(c => c.ParentCommentId == null);
EF Core将其转换为以下内容:
exec sp_executesql N'SELECT [c].[CommentId], [c].[Content], [c].[ParentCommentId]
FROM [Comment] AS [c]
WHERE [c].[PostId] = @__request_PostId_0',N'@__request_PostId_0 uniqueidentifier',@__request_PostId_0='post-id-here'
递归CTE返回平面列表,然后您必须再次构建层次结构 var commentHierarchy=wait_context.Comment .FromSqlInterpolated$SELECT CommentId,Content,PostId,ParentCommentId FROM dbo.fn_post-commenthierarchy'post-id-here' .ProjectTo_mapper.ConfigurationProvider .ToListAsync; var lookup=commentHierarchy.ToLookupx=>x.commentId; 注释层次结构中的foreach var c { 如果lookup.Containsc.commentId c、 inverseParentComment.AddRangelookup.Item[c.commentId]; } var result=commentHierarchy.Wherec=>c.parentCommentId==null;
谢谢你的主意。我更新了我的问题,请看一下您调用的递归CTE,不要从数据库加载整个表。只需要一片叶子。@SvyatoslavDanyliv,你认为我为什么要把整张桌子都装上?我再次更新了我的问题,以显示EF Core发送到数据库的SQL查询是的,如果您可以通过PostId进行筛选,则不需要CTE。谢谢。我会确认你的回答。当您必须使用递归CTE时,可以使用它。但我认为我的案例可以简化解决方案。
[
{
"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": "0bb77a43-c7bb-482f-9bf8-55c4050974da",
"content": "Sure",
"postId": "69f3ca3a-66fc-4142-873d-01e950d83adf",
"post": null,
"parentCommentId": "59656765-d1ed-4648-8696-7d576ab7419f",
"commentRates": [],
"inverseParentComment": []
},
{
"commentId": "b8d61cfd-d274-4dae-a2be-72e08cfa9066",
"content": "What?",
"postId": "69f3ca3a-66fc-4142-873d-01e950d83adf",
"post": null,
"parentCommentId": "59656765-d1ed-4648-8696-7d576ab7419f",
"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": []
}
]
},
{
"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",
"parentComment": {
"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": []
},
"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",
"parentComment": {
"commentId": "be02742a-9170-4335-afe7-3c7c22684424",
"content": "Hello World!",
"postId": "69f3ca3a-66fc-4142-873d-01e950d83adf",
"post": null,
"parentCommentId": null,
"parentComment": null,
"commentRates": [],
"inverseParentComment": []
},
"commentRates": [],
"inverseParentComment": [
{
"commentId": "0bb77a43-c7bb-482f-9bf8-55c4050974da",
"content": "Sure",
"postId": "69f3ca3a-66fc-4142-873d-01e950d83adf",
"post": null,
"parentCommentId": "59656765-d1ed-4648-8696-7d576ab7419f",
"commentRates": [],
"inverseParentComment": []
},
{
"commentId": "b8d61cfd-d274-4dae-a2be-72e08cfa9066",
"content": "What?",
"postId": "69f3ca3a-66fc-4142-873d-01e950d83adf",
"post": null,
"parentCommentId": "59656765-d1ed-4648-8696-7d576ab7419f",
"commentRates": [],
"inverseParentComment": []
}
]
},
{
"commentId": "0bb77a43-c7bb-482f-9bf8-55c4050974da",
"content": "Sure",
"postId": "69f3ca3a-66fc-4142-873d-01e950d83adf",
"post": null,
"parentCommentId": "59656765-d1ed-4648-8696-7d576ab7419f",
"parentComment": {
"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",
"parentComment": {
"commentId": "be02742a-9170-4335-afe7-3c7c22684424",
"content": "Hello World!",
"postId": "69f3ca3a-66fc-4142-873d-01e950d83adf",
"post": null,
"parentCommentId": null,
"parentComment": null,
"commentRates": [],
"inverseParentComment": []
},
"commentRates": [],
"inverseParentComment": [
{
"commentId": "b8d61cfd-d274-4dae-a2be-72e08cfa9066",
"content": "What?",
"postId": "69f3ca3a-66fc-4142-873d-01e950d83adf",
"post": null,
"parentCommentId": "59656765-d1ed-4648-8696-7d576ab7419f",
"commentRates": [],
"inverseParentComment": []
}
]
},
"commentRates": [],
"inverseParentComment": []
},
{
"commentId": "b8d61cfd-d274-4dae-a2be-72e08cfa9066",
"content": "What?",
"postId": "69f3ca3a-66fc-4142-873d-01e950d83adf",
"post": null,
"parentCommentId": "59656765-d1ed-4648-8696-7d576ab7419f",
"parentComment": {
"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",
"parentComment": {
"commentId": "be02742a-9170-4335-afe7-3c7c22684424",
"content": "Hello World!",
"postId": "69f3ca3a-66fc-4142-873d-01e950d83adf",
"post": null,
"parentCommentId": null,
"parentComment": null,
"commentRates": [],
"inverseParentComment": []
},
"commentRates": [],
"inverseParentComment": [
{
"commentId": "0bb77a43-c7bb-482f-9bf8-55c4050974da",
"content": "Sure",
"postId": "69f3ca3a-66fc-4142-873d-01e950d83adf",
"post": null,
"parentCommentId": "59656765-d1ed-4648-8696-7d576ab7419f",
"commentRates": [],
"inverseParentComment": []
}
]
},
"commentRates": [],
"inverseParentComment": []
}]
List<CommentDto> commentHierarchy = await _context.Comment
.FromSqlInterpolated($"SELECT CommentId, Content, PostId, ParentCommentId FROM dbo.fn_PostCommentHierarchy('post-id-here')")
.ProjectTo<CommentDto>(_mapper.ConfigurationProvider)
.ToListAsync();
[
{
"commentId": "be02742a-9170-4335-afe7-3c7c22684424",
"content": "Hello World!",
"parentCommentId": null,
"inverseParentComment": [
{
"commentId": "59656765-d1ed-4648-8696-7d576ab7419f",
"content": "Are you a programmer?",
"parentCommentId": "be02742a-9170-4335-afe7-3c7c22684424",
"inverseParentComment": null
}
]
},
{
"commentId": "cfe126b3-4601-4432-8c87-445c1362a225",
"content": "I wanna go to Mars too!",
"parentCommentId": null,
"inverseParentComment": [
{
"commentId": "ab6d6b49-d772-48cd-9477-8d40f133c37a",
"content": "See you on the Moon :)",
"parentCommentId": "cfe126b3-4601-4432-8c87-445c1362a225",
"inverseParentComment": null
}
]
},
{
"commentId": "0bb77a43-c7bb-482f-9bf8-55c4050974da",
"content": "Sure",
"parentCommentId": "59656765-d1ed-4648-8696-7d576ab7419f",
"inverseParentComment": []
},
{
"commentId": "b8d61cfd-d274-4dae-a2be-72e08cfa9066",
"content": "What?",
"parentCommentId": "59656765-d1ed-4648-8696-7d576ab7419f",
"inverseParentComment": []
},
{
"commentId": "59656765-d1ed-4648-8696-7d576ab7419f",
"content": "Are you a programmer?",
"parentCommentId": "be02742a-9170-4335-afe7-3c7c22684424",
"inverseParentComment": [
{
"commentId": "0bb77a43-c7bb-482f-9bf8-55c4050974da",
"content": "Sure",
"parentCommentId": "59656765-d1ed-4648-8696-7d576ab7419f",
"inverseParentComment": null
},
{
"commentId": "b8d61cfd-d274-4dae-a2be-72e08cfa9066",
"content": "What?",
"parentCommentId": "59656765-d1ed-4648-8696-7d576ab7419f",
"inverseParentComment": null
}
]
},
{
"commentId": "ab6d6b49-d772-48cd-9477-8d40f133c37a",
"content": "See you on the Moon :)",
"parentCommentId": "cfe126b3-4601-4432-8c87-445c1362a225",
"inverseParentComment": []
}
]
SELECT [c].[CommentId], [c].[Content], [c].[ParentCommentId], [c0].[CommentId], [c0].[Content], [c0].[ParentCommentId]
FROM (
SELECT CommentId, Content, PostId, ParentCommentId FROM dbo.fn_PostCommentHierarchy('69f3ca3a-66fc-4142-873d-01e950d83adf')
) AS [c]
LEFT JOIN [Comment] AS [c0] ON [c].[CommentId] = [c0].[ParentCommentId]
ORDER BY [c].[CommentId], [c0].[CommentId]
List<CommentDto> commentFlatList = await _context.Comment
.Where(c => c.PostId == Guid.Parse("post-id-here"))
.ProjectTo<CommentDto>(_mapper.ConfigurationProvider)
.ToListAsync();
Dictionary<Guid, CommentDto> commentDictionary = commentFlatList
.ToDictionary(c => c.CommentId);
foreach (var comment in commentFlatList)
{
if (comment.ParentCommentId == null)
{
continue;
}
if (commentDictionary.TryGetValue((Guid) comment.ParentCommentId, out CommentDto parent))
{
parent.Children.Add(comment);
}
}
List<CommentDto> commentHierarchy = commentFlatList.Where(c => c.ParentCommentId == null);
List<CommentDto> commentFlatList = await _context.Comment
.Where(c => c.PostId == Guid.Parse("post-id-here"))
.ProjectTo<CommentDto>(_mapper.ConfigurationProvider)
.ToListAsync();
exec sp_executesql N'SELECT [c].[CommentId], [c].[Content], [c].[ParentCommentId]
FROM [Comment] AS [c]
WHERE [c].[PostId] = @__request_PostId_0',N'@__request_PostId_0 uniqueidentifier',@__request_PostId_0='post-id-here'