我正在撰写评论功能。你可以评论一个线程。你也可以对评论发表评论。因此,Thread有一个注释列表,每个注释都有一个注释列表:
public class Thread
{
public int ThreadId { get; set; }
public int PostId { get; set; }
public virtual List<Comment> Comments { get; set; }
}
public class Comment
{
public int CommentId { get; set; }
[MaxLength(280)]
public string Content { get; set; }
public long time { get; set; } // unix time / epoch time
[JsonIgnore]
[IgnoreDataMember]
public string user_id { get; set; }
public virtual CommentUser CommentUser { get; set; }
[JsonIgnore]
[IgnoreDataMember]
public int ThreadId { get; set; }
[JsonIgnore]
[IgnoreDataMember]
public virtual Thread Thread { get; set; }
public virtual List<Comment> Comments { get; set; }
}
我遇到的问题是,当我评论一个帖子然后评论该评论时。所以总共有两条评论。
我得到了这个JSON,有三条评论:
{
"ThreadId": 3,
"Comments": [
{
"Comments": [
{
"Comments": [],
"CommentUser": {
"user_id": "123",
"nickname": "user",
},
"CommentId": 52,
"Content": "comment on comment",
"time": 1513784459
}
],
"CommentUser": {
"user_id": "123",
"nickname": "user",
},
"CommentId": 51,
"Content": "asdf asdf asd f",
"time": 1513784447
},
{
// THIS COMMENT SHOULD NOT SHOW UP HERE
"Comments": [],
"CommentUser": {
"user_id": "123",
"nickname": "user",
},
"CommentId": 52,
"Content": "comment on comment",
"time": 1513784459
}
]
}
这就是DB的样子,只有两条评论:
获取线程的方法非常简单:
Thread commentThread = await (from t in db.Thread where t.PostId == tr.postId select t).FirstOrDefaultAsync();
return Json(commentThread);
我努力弄清楚哪里出了问题。任何帮助赞赏!
答案 0 :(得分:2)
@Ivan&amp; @Olivier在评论中提到,这是因为Comment
实体与所有者Thread
有直接关系(必需),而与级别无关。
基本上,当您执行linq查询时,结果是具有两个Thread
的{{1}}对象。第二个是您在第一条评论评论中可以看到的相同评论。哦,孩子,这是一口。
这样的事情:
Comments
避免此问题的一种方法是仅从顶级注释引用线程,并在所有嵌套注释上将ThreadId设置为null。这将为您提供最佳结果,因为您的查询将保持干净(即您无需更改代码以过滤或排除,也无需检查是否引用了注释)。
但是您当前的模型不允许这样做,因为您的ThreadId是commentThread.Comments = Comment[]
//first comment from thread
{
"CommentId": 1,
"Content": "Thread comment",
"Comments": [
//This is the same comment (Id = 2) being fetched from both Thread and Comment entities.
{
"CommentId": 2,
"Content": "Comment on comment"
}
]
},
//second comment from thread
{
"CommentId": 2,
"Content": "Comment on comment"
}
]
。根据您向我们展示的模型数量,这应该相对容易实现。所以只需将属性int
设置为ThreadId
:
int?
运行迁移并更新您的Db(或获取DBA的脚本):
add-migration SetThreadIdAsNullable
更新数据库或更新数据库-script
你应该好好去。现在,查询应返回public class Comment
{
public int CommentId { get; set; }
[MaxLength(280)]
public string Content { get; set; }
public long time { get; set; } // unix time / epoch time
[JsonIgnore]
[IgnoreDataMember]
public string user_id { get; set; }
public virtual CommentUser CommentUser { get; set; }
[JsonIgnore]
[IgnoreDataMember]
public int? ThreadId { get; set; }
^ //set this as nullable
[JsonIgnore]
[IgnoreDataMember]
public virtual Thread Thread { get; set; }
public virtual List<Comment> Comments { get; set; }
}
个对象,其中只有一个thread
,而后者应该包含嵌套的comment
,如您所希望的那样。
希望这有帮助!
答案 1 :(得分:1)
这是Thread
实体的通常行为。因为,在评论表中有两个ThreadId = 3
的记录。因此,您应该删除Comment_CommentId
非空的注释记录。所以,也许你可以用扩展方法过滤它;
public static class ThreadExtensions
{
public static IQueryable<Thread> GetThreadAsQueryable(this DbSet<Thread> table)
{
return table.Where(x => x.Comments.Where(x => x.Comment_CommentId == null));
}
}
using (var context = new SampleDbContext())
{
var str = context.Thread.GetThreadAsQueryable().ToList();
}
此外,还有一项功能为 EfCore 2.0 命名为HasQueryFilter
。我认为,它完全满足需要。如果您使用EfCore 2.0,我建议您检查it。