使用实体框架7代码优先定义自引用外键关系

时间:2015-12-23 11:48:34

标签: entity-framework-core ef-fluent-api

我有一个ArticleComment实体,如下所示:

public class ArticleComment
 {
    public int ArticleCommentId { get; set; }
    public int? ArticleCommentParentId { get; set; }

    //[ForeignKey("ArticleCommentParentId")]
    public virtual ArticleComment Comment { get; set; }
    public DateTime ArticleDateCreated  { get; set; }
    public string ArticleCommentName { get; set; }
    public string ArticleCommentEmail { get; set; }
    public string ArticleCommentWebSite { get; set; }
    public string AricleCommentBody { get; set; }

    //[ForeignKey("UserIDfk")]   
    public virtual ApplicationUser ApplicationUser { get; set; }
    public Guid? UserIDfk { get; set; }

    public int ArticleIDfk { get; set; }
    //[ForeignKey("ArticleIDfk")]   
    public virtual Article Article { get; set; }


}

我想以这样的方式定义外键关系,即一个注释可以有很多回复或者孩子,我试图使用流畅的API创建关系,如下所示:

builder.Entity<ArticleComment>()
            .HasOne(p => p.Comment)
            .WithMany()
            .HasForeignKey(p => p.ArticleCommentParentId)
            .OnDelete(DeleteBehavior.Restrict)
            .IsRequired(false);

我按照提出的解决方案herehere,但收到错误消息:

  

在表'ArticleComment'上引入FOREIGN KEY约束'FK_ArticleComment_ArticleComment_ArticleCommentParentId'可能会导致循环或多个级联路径。指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。   无法创建约束或索引。查看以前的错误。

首先我设置OnDelete(DeleteBehavior.Restrict)这会消失,但问题仍然存在,我也尝试使用数据注释[ForeignKey("ArticleCommentParentId")],因为您可以看到{{1}中的注释代码1}}定义,但它不起作用,我对此感激不尽。

2 个答案:

答案 0 :(得分:11)

您没有正确建模您的实体。每个评论都需要一个 Set 的回复,这些回复也是ArticleComment类型,并且每个回复都是指向其父回复的回复(请注意添加的 ICollection回复属性 ):

  public class ArticleComment
     {
        public ArticleComment()
        {
            Replies = new HashSet<ArticleComment>();
        }

        public int ArticleCommentId { get; set; }
        public int? ParentArticleCommentId { get; set; }
        public virtual ArticleComment ParentArticleComment{ get; set; }
        public virtual ICollection<ArticleComment> Replies { get; set; }

        //The rest of the properties omitted for clarity...
    }

...和流畅的映射:

modelBuilder.Entity<ArticleComment>(entity =>
{
    entity
        .HasMany(e => e.Replies )
        .WithOne(e => e.ParentArticleComment) //Each comment from Replies points back to its parent
        .HasForeignKey(e => e.ParentArticleCommentId );
});

通过上述设置,您将获得一个开放式树结构。

编辑: 使用属性,您只需要装饰ParentArticleComment属性。 考虑到在这种情况下EF将按惯例解决所有关系。

[ForeignKey("ParentArticleCommentId")]
public virtual ArticleComment ParentArticleComment{ get; set; } 

对于集合属性,EF非常聪明,可以理解这种关系。

答案 1 :(得分:1)

我简化了类(删除外键支持字段)并且它有效 这可能是你的EF版本的一个问题(我刚刚安装了它,但实际上我认为我使用的是rc1,但我不确定,因为我有几个依赖性问题)或者它可能是你的模型。
无论如何,这个源工作正常

public class ArticleComment
{
    public int ArticleCommentId { get; set; }

    public virtual ArticleComment Comment { get; set; }
    public DateTime ArticleDateCreated { get; set; }
    public string ArticleCommentName { get; set; }
    public string ArticleCommentEmail { get; set; }
    public string ArticleCommentWebSite { get; set; }
    public string AricleCommentBody { get; set; }
}

class Context : DbContext
{
    public Context(DbContextOptions dbContextOptions) : base(dbContextOptions)
    {}

    public DbSet<ArticleComment> Comments { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<ArticleComment>()
            .HasOne(p => p.Comment)
            .WithMany();

    }
}

static class SampleData
{
    public static void Initialize(Context context)
    {
        if (!context.Comments.Any())
        {
            var comment1 = new ArticleComment()
            {
                AricleCommentBody = "Article 1"
            };

            var comment2 = new ArticleComment()
            {
                AricleCommentBody = "Article 2 that referes to 1",
                Comment = comment1
            };

            context.Comments.Add(comment2);
            context.Comments.Add(comment1);

            context.SaveChanges();
        }
    }
}