代码优先 - 自我引用一对多关系

时间:2012-10-20 14:45:42

标签: c# entity-framework code-first

我在这里发现了很多类似的问题,但似乎没有一个问题可以解决我的问题。流利的api&属性没有帮助。数据库已创建,但在向其添加对象时,它已崩溃。 我想要一个拥有自己的集合的类。这是我的代码:

[Table("UObjects")]
public class UObject
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Browsable(false)]
    public long ID { get; set; }
    public string Name { get; set; }
    [Browsable(false)]
    public long? ParentID { get; set; }

    public virtual UObject UParent { get; set; }
    [Browsable(false)]
    public virtual ICollection<UObject> UObjects { get; set; }
}


public class MyContext : DbContext
{
    public DbSet<UObject> UObjects { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // This fluent API didn't help
        //modelBuilder.Entity<UObject>()
        //        .HasOptional(u => u.UParent)
        //        .WithMany(u => u.UObjects)
        //        .HasForeignKey(u => u.ParentID);

        //modelBuilder.Entity<UObject>()
        //        .HasOptional(u => u.UParent)
        //        .WithMany(u => u.UObjects)
        //        .Map(c =>
        //        {
        //            c.MapKey("ParentID");
        //            c.ToTable("UObjects");
        //        });
    }
}

数据库中的记录是这样的:

ID | Name       | ParentID
------------------------------------
1  | First      | 0
2  | SubFirst   | 1
3  | SubSecond  | 1
4  | SubThird   | 2
5  | SubFourth  | 2

接下来在加载实体后我的对象应如何看待:

   - First
      - SubFirst
         - SubThird
         - SubFourth
      - SubSecond

但是每个对象都有一个空集合。我该怎么做才能让它正常工作?

3 个答案:

答案 0 :(得分:6)

您只需要通过更正字段而不是像这样导航属性来提及自引用:

 [ForeignKey("UParent")]    // EF need for self reference
 public long? ParentID { get; set; }

在构造函数中初始化导航属性,如下所示:

  public UObject()       
    {
        // this is necessary otherwise EF will throw null object reference error. you could also put ?? operator check for more interactive solution.  
        UObjects = new List<UObject>(); 
    }

并且还需要像你一样覆盖,但是像这样:

   protected override void OnModelCreating(DbModelBuilder modelBuilder)     
    {   
        // folowwing is also necessary in case you're using identity model     
        base.OnModelCreating(modelBuilder);               
        modelBuilder.Entity<UObjects>()       
            .HasOptional<UObjects>(u => u.UParent) // EF'll load Parent if any     
            .WithMany(u => u.UObjects);        // load all childs if any 
    }

答案 1 :(得分:1)

与您的几乎相同的实体类适用于EF Core。我将您的属性ParentID重命名为UParentID并添加了构造函数。

[Table("UObjects")]
public class UObject
{
  protected UObject()
  {
    UObjects = new List<UObject>();
  }

  public UObject(UObject parent, string name)
    : this()
  {
    Name = name;
    UParent = parent;
    UParent?.UObjects.Add(this);
  }

  [Key]
  [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
  public long ID { get; set; }
  public string Name { get; set; }
  public long? UParentID { get; set; }

  public virtual UObject UParent { get; set; }
  public virtual ICollection<UObject> UObjects { get; set; }
}

ApplicationDBContext我只有这个:

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);
    builder.Entity<UObject>();
}

用法(请参阅如何使用正确的值填充root对象的属性): Eager loading

注意:我没有在此代码中删除删除。如果你需要它,事情可能会变得更复杂。

答案 2 :(得分:-3)

  1. 使用ForeignKey属性装饰您的UParent属性?因为它可以是可以为空的
  2. [ForeignKey("ParentID")] public virtual UObject? UParent { get; set; }

    1. 在数据库中:如果没有父级,则将ParentId值设置为“NULL”。