2012-10-20 14 views
6

Ho trovato molte domande simili qui, ma nessuno di loro sembra aiutarmi con il mio problema. Gli attributi di Fluent api & non hanno aiutato. Il database è stato creato, ma quando si aggiunge un oggetto, si è arrestato in modo anomalo. Voglio avere una classe che abbia una collezione di se stessa. Ecco il codice che ho:Code First - Self-referencing one to many relation

[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 nel database sono come questo:

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

Così come il mio oggetto dovrebbe apparire dopo aver caricato le entità è prossimo:

- First 
     - SubFirst 
     - SubThird 
     - SubFourth 
     - SubSecond 

Ma ogni oggetto ha una collezione vuota. Cosa devo fare per farlo funzionare correttamente?

+0

controllare questo http://stackoverflow.com/questions/10421351/many-to-many-relationship-between-entities-of-same-type-in-mvc3/10422172#10422172 – Shyju

+0

provato già questo. Ha creato il database ma si blocca quando si aggiunge un nuovo oggetto e si chiama SaveChanges() – GaaRa

+5

"* si blocca *" non è proprio una descrizione del problema. 1) Qual è esattamente l'eccezione? 2) Quale query hai eseguito quando hai caricato le entità con raccolte vuote? 3) Perché hai un 'ParentID' di' 0' nella prima riga del database? Viola un vincolo referenziale (non c'è riga con 'ID' 0). O vuoi dire 'NULL'? – Slauma

risposta

-3
  1. Decora la proprietà UParent con l'attributo ForeignKey e? poiché può essere annullabile

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

  1. in database: il valore Set ParentId a 'NULL' se non v'è nessun genitore.
+2

Il? non è valido, poiché UObject è una classe, non un tipo di valore. –

5

Hai solo bisogno di menzionato il riferimento sé correggendo sul campo piuttosto che sulla navigazione proprietà come questa:

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

E nel costruttore inizializzare le proprietà di navigazione come questa:

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

E anche bisogno di eseguire l'override come stavi facendo ma in questo modo:

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 
    } 
+0

Sto raggiungendo lo stesso comportamento per categorie e sottocategorie e molti luoghi in cui è necessario l'autoreferenzialità. Sentirsi libero se non si ottengono risultati desiderati –

+0

L'inizializzazione della raccolta nel costruttore non è strettamente necessaria. EF lo creerà quando è nullo. –

1

Una classe di entità quasi identica al tuo funziona in EF Core. Ho rinominato la tua proprietà ParentID in UParentID e aggiunto i costruttori.

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

In ApplicationDBContext ho solo questo:

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

Uso (vedere come le proprietà dell'oggetto root sono riempiti con valori corretti): Eager loading

Nota: non l'ho fatto preoccuparsi della cancellazione in questo codice. Se ne hai bisogno, le cose diventeranno probabilmente più complicate.

Problemi correlati