Code First - 自引用一对多关系

Posted

技术标签:

【中文标题】Code First - 自引用一对多关系【英文标题】:Code First - Self-referencing one to many relation 【发布时间】:2012-10-20 14:45:42 【问题描述】:

我在这里发现了很多类似的问题,但似乎没有一个可以帮助我解决我的问题。流利的 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

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

【问题讨论】:

查看***.com/questions/10421351/… 已经试过了。它创建了数据库,但在添加新对象并调用 SaveChanges() 时崩溃 "it crashed" 并不是一个很好的问题描述。 1)究竟是什么异常? 2) 当您加载带有空集合的实体时,您运行了什么查询? 3) 为什么在第一个数据库行中有0ParentID?它违反了引用约束(没有ID 0 的行)。还是你的意思是NULL 哦,我现在明白了...你说得对,问题是我的根元素 ParentID 等于 0,它应该是 null 看到这个问题:***.com/questions/11565423/… 【参考方案1】:

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

 [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 a 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 
    

【讨论】:

我在类别和子类别以及许多需要自我引用的地方实现了相同的行为。如果仍然没有得到想要的结果,请随意 在构造函数中初始化集合并不是绝对必要的。 EF 会在它为 null 时创建它。【参考方案2】:

与您的几乎相同的实体类在 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 对象的属性如何填充正确的值):

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

【讨论】:

【参考方案3】:
    使用 ForeignKey 属性和 ?因为它可以为空

[ForeignKey("ParentID")] public virtual UObject? UParent get; set;

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

【讨论】:

那个?无效,因为 UObject 是一个类,而不是值类型。

以上是关于Code First - 自引用一对多关系的主要内容,如果未能解决你的问题,请参考以下文章

EF Code First 导航属性 与外键

EF Code First 导航属性 与外键

EntityFramework Code-First 简易教程-------一对多

如何使用Entity Framework 4.1 Code First为数据库中的一对多关系强制一对一关系

Entity Framework Code First 从两个表和一对多关系创建类

Entity Framework 4.1 RTW Code First - POCO 一对多是不是需要引用子实体和子实体主键?