Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Code First - Self-referencing one to many relation

I've found a lot of similar questions here, but none of them seems to help me with my problem. Fluent api & attributes didn't help. The database was created, but when adding an object to it, it crashed. I want to have a class that has a collection of itself. Here's the code I have:

[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");
        //        });
    }
}

Records in database are like this:

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

So how my object should look after loading the entities is next:

   - First
      - SubFirst
         - SubThird
         - SubFourth
      - SubSecond

But every object has an empty collection. What should I do to make it work properly?

like image 873
GaaRa Avatar asked Oct 20 '12 14:10

GaaRa


1 Answers

You only need to mention the self reference by correcting on field rather than on navigating property like this:

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

And in constructor, initialize navigation properties like this:

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

And also need to override as you were doing but like this:

   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 
    }
like image 86
Mohtisham Zubair Avatar answered Nov 14 '22 23:11

Mohtisham Zubair