C# 代码优先实体框架-注释功能上的注释,同一注释显示两次

C# 代码优先实体框架-注释功能上的注释,同一注释显示两次,c#,sql,json,entity-framework,ef-code-first,C#,Sql,Json,Entity Framework,Ef Code First,我正在制作一个评论功能。你可以在一个帖子上发表评论。你也可以对评论发表评论。所以线程有一个注释列表,每个注释都有一个注释列表: public class Thread { public int ThreadId { get; set; } public int PostId { get; set; } public virtual List<Comment> Comments { get; set; } } public class Comment {

我正在制作一个评论功能。你可以在一个帖子上发表评论。你也可以对评论发表评论。所以线程有一个注释列表,每个注释都有一个注释列表:

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; }

}
DB是这样的,只有两条注释:

获取线程的方法非常简单:

        Thread commentThread = await (from t in db.Thread where t.PostId == tr.postId select t).FirstOrDefaultAsync();

        return Json(commentThread);

我努力想弄清楚这出了什么问题。感谢您的帮助

正如@Ivan和@Olivier在他们的评论中提到的那样,之所以会出现这种情况,是因为
评论
实体与所有者
线程
有着直接的关系(必需的),无论级别如何

基本上,当您执行linq查询时,结果是带有两条注释的
线程
对象。第二条评论与第一条评论相同。哦,孩子,这是一口

大概是这样的:

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"
    }
]
避免此问题的一种方法是只引用顶级注释中的线程,并在所有嵌套注释上将ThreadId设置为null。这将为您提供最佳结果,因为您的查询将保持干净(即您不需要更改代码以进行筛选或排除,也不需要检查是否引用了注释)

但是您当前的型号不允许这样做,因为您的ThreadId是
int
。根据您向我们展示的模型数量,这应该相对容易实现。因此,只需将属性
ThreadId
设置为
int?

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; }

}
公共类注释
{
public int CommentId{get;set;}
[MaxLength(280)]
公共字符串内容{get;set;}
公共长时间{get;set;}//unix时间/纪元时间
[JsonIgnore]
[IgnoreDataMember]
公共字符串用户_id{get;set;}
公共虚拟CommentUser CommentUser{get;set;}
[JsonIgnore]
[IgnoreDataMember]
public int?ThreadId{get;set;}
^//将其设置为可空
[JsonIgnore]
[IgnoreDataMember]
公共虚拟线程线程{get;set;}
公共虚拟列表注释{get;set;}
}
运行迁移并更新数据库(或为DBA获取脚本):

添加迁移SetThreadIdAsNullable

更新数据库更新数据库-脚本

你应该可以走了。查询现在应该返回
线程
对象,其中只有一条
注释
,而这条注释又应该包含一条嵌套的
注释
,正如您所希望的那样


希望这有帮助

这是
线程
实体的常见行为。因为,在注释表中有两条记录
ThreadId=3
。因此,您应该删除
Comment\u 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();
}
公共静态类线程扩展
{
公共静态IQueryable GetThreadAsQueryable(此DbSet表)
{
返回table.Where(x=>x.Comments.Where(x=>x.Comment_CommentId==null));
}
}
使用(var context=new SampleDbContext())
{
var str=context.Thread.GetThreadAsQueryable().ToList();
}

另外,还有一个功能名为
haskqueryfilter
,用于efcore2.0。我想,正是满足了这一需要。如果您使用EfCore 2.0,我建议您检查一下

每个“评论上的评论”都会出现两次,因为它是
线程.Comments
评论.Comments
的一部分。这是模型的正常行为。更改模型或使用带过滤器的投影查询(尽管后者可能会对递归数据模型产生问题)。它确实会显示在那里,因为注释上的注释直接引用了
ThreadId=3
的线程。如果只有顶级注释引用了线程,就不会发生这种情况。JSON无法引用嵌套注释,因此会重复注释。这非常有帮助!非常感谢你。它解决了我的问题:)
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();
}