c# - 加载相关数据——递归注释
问题描述
语境
我有一个使用 Razor Pages 构建的简单链接聚合器 ASP.NET Core 应用程序。
每个链接的详细信息页面显示评论:
用户可以评论链接并回复评论。
楷模
这是Link
课程:
public class Link
{
public int Id { get; set; }
public string UserId { get; set; }
[DataType(DataType.Url)]
public string Url { get; set; }
public string Title { get; set; }
[Display(Name = "Date")]
[DataType(DataType.Date)]
public DateTime DateTime { get; set; }
[ForeignKey("UserId")]
public IdentityUser User { get; set; }
public List<Vote> Votes { get; set; }
public int Score() => Votes.Sum(vote => vote.Score);
public Vote UserVote(string userId) =>
Votes.FirstOrDefault(vote => vote.UserId == userId);
public int UserScore(string userId)
{
var vote = UserVote(userId);
return vote == null ? 0 : vote.Score;
}
public async Task Vote(int score, string voterUserId)
{
var vote = UserVote(voterUserId);
if (vote == null)
{
vote = new Vote()
{
UserId = voterUserId,
LinkId = Id,
Score = score,
DateTime = DateTime.Now
};
Votes.Add(vote);
}
else
{
vote.Score = vote.Score == score ? 0 : score;
}
}
public List<Comment> Comments { get; set; }
public async Task AddComment(string text, string commenterUserId)
{
var comment = new Comment()
{
UserId = commenterUserId,
LinkId = Id,
Text = text,
DateTime = DateTime.Now
};
Comments.Add(comment);
}
}
这是Comment
课程:
public class Comment
{
public int Id { get; set; }
public int? LinkId { get; set; }
//[ForeignKey("LinkId")]
public Link Link { get; set; }
public int? ParentCommentId { get; set; }
//[ForeignKey("ParentCommentId")]
public Comment ParentComment { get; set; }
public List<Comment> Comments { get; set; }
public string Text { get; set; }
public DateTime DateTime { get; set; }
public string UserId { get; set; }
[ForeignKey("UserId")]
public IdentityUser User { get; set; }
public List<CommentVote> Votes { get; set; }
public int Score() =>
Votes
.Where(vote => vote.CommentId == Id)
.Sum(vote => vote.Score);
public CommentVote UserVote(string userId) =>
Votes.FirstOrDefault(vote => vote.UserId == userId);
public int UserScore(string userId)
{
var vote = UserVote(userId);
return vote == null ? 0 : vote.Score;
}
public async Task Vote(int score, string voterUserId)
{
var vote = UserVote(voterUserId);
if (vote == null)
{
vote = new CommentVote()
{
UserId = voterUserId,
CommentId = Id,
Score = score,
DateTime = DateTime.Now
};
Votes.Add(vote);
}
else
{
vote.Score = vote.Score == score ? 0 : score;
}
}
public async Task AddComment(string text, string commenterUserId)
{
var comment = new Comment()
{
UserId = commenterUserId,
ParentCommentId = Id,
Text = text,
DateTime = DateTime.Now
};
Comments.Add(comment);
}
}
Details
剃刀页面
这是页面的OnGetAsync
方法Details
。
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Link = await _context.Link
.Include(link => link.User)
.Include(link => link.Votes)
.Include(link => link.Comments)
.FirstOrDefaultAsync(m => m.Id == id);
void load_comments(List<Comment> comments)
{
foreach (var comment in comments)
{
_context.Entry(comment).Reference(comment => comment.User).Load();
_context.Entry(comment).Collection(comment => comment.Comments).Load();
_context.Entry(comment).Collection(comment => comment.Votes).Load();
load_comments(comment.Comments);
}
}
load_comments(Link.Comments);
if (Link == null)
{
return NotFound();
}
return Page();
}
如您所见,我Include
使用Link
.
当用户向 a 添加评论时Link
,LinkId
的Comment
有一个值。当用户向 a 添加评论时Comment
,ParentCommentId
的Comment
有一个值。
由于评论可以有评论,我使用递归内部函数load_comments
通过显式加载检索评论树。
建议的方法
Google 搜索ef core 包括递归导致加载递归实体的各种方法。
这个关于 stackoverflow 的答案推荐了一种递归函数方法,有点类似于我上面展示的方法。
问题
有没有办法获取Link
使用急切加载而不是显式加载的评论?与显式递归方法相比,这似乎是一个更简单的实现。
上面的第二个答案表明它应该起作用。但是,在加载评论页面时,实施该建议(及其一些变体)会导致运行时错误。
完整项目
完整的项目可在 github 上找到:LinkAggregatorComments。这只是项目的快照,用于此类问题。
链接到上面讨论的一些部分:
解决方案
如果所有子评论都引用了该链接,那么您根本不需要该load_comment
部分。EF 在将父子关系加载到上下文时修复它们,并Include(link => link.Comments)
加载所有它们。
加载评论的投票和用户ThenInclude
。
推荐阅读
- javascript - 如何防止组件重新渲染?(与 MWE)
- c - 在 intel x86 上为裸机开发编写 C 函数,链接器报告溢出
- excel - 获取运行时错误“-2147418113 (8000ffff)”
- awk - 使用 awk/sed 更改列
- unity3d - 实例化 GameObjects 并在它撞到地面时更改材质
- html - CSS object-fit 拉伸图像
- redis - 使用redis作为我的核心BD时我的业务逻辑放在哪里
- arrays - 如何从开关中获取多个值
- html - “文本下划线偏移”在“显示:块”中不起作用
- swift - 'NSButton' 类型的值没有成员 'Clicked' | 带有按钮的 Xcode 错误