2011-11-22 13 views
5

ho una relazione molti-a-molti in EF Codice-First (come spiegato nel this questione), e voglio usare un uno-a -molto alla stessa entità pure. Il problema è che EF non produce il giusto schema di database. Codice:Utilizzando sia molti-a-molti e uno-a-molti per stessa entità

public class A 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public virtual ICollection<B> ObjectsOfB { get; set; } 
} 

public class B 
{ 
    public int Id { get; set; } 
    public virtual A ObjectA { get; set; } 
    public virtual ICollection<A> OtherObjectsOfA { get; set; } 
} 

Quando rimuovo la proprietà ObjectA della classe B, l'associazione many-to-many viene generata correttamente. Se generato in modo errato, l'entità B ottiene 2 chiavi esterne in A, e l'entità A ottiene 1 chiave esterna in B (come una relazione molti-a-uno).

risposta

15

Se si dispone di più di una proprietà di navigazione riferendosi alla stessa entità EF non sa dove la proprietà di navigazione inversa sul altra entità appartiene. Nel tuo esempio: A.ObjectsOfB si riferisce a B.ObjectA o a B.OtherObjectsOfA? Entrambi sarebbero possibili e un modello valido.

Ora, EF non genera un'eccezione del tipo "non può determinare le relazioni in modo inequivocabile", o qualcosa del genere. Decide invece che B.ObjectA fa riferimento a un terzo endpoint in B che non è esposto come proprietà di navigazione nel modello. Ciò crea la prima chiave esterna nella tabella B. Le due proprietà di navigazione in B si riferiscono a due endpoint in A che non sono esposti nel modello: B.ObjectA crea la seconda chiave esterna nella tabella B e B.OtherObjectsOfA crea una chiave esterna nella tabella A.

Per risolvere questo problema è necessario specificare le relazioni in modo esplicito.

Opzione uno (il modo più semplice) è quello di utilizzare l'attributo InverseProperty:

public class A 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    [InverseProperty("OtherObjectsOfA")] 
    public virtual ICollection<B> ObjectsOfB { get; set; } 
} 

Definisce che A.ObjectsOfB fa parte di un molti-a-molti relazione B.OtherObjectsOfA.

L'altra opzione è quella di definire completamente le relazioni in API Fluent:

protected override void OnModelCreating(DbModelBuilder modelBuilder) 
{ 
    modelBuilder.Entity<A>() 
     .HasMany(a => a.ObjectsOfB) 
     .WithMany(b => b.OtherObjectsOfA) 
     .Map(x => 
     { 
      x.MapLeftKey("AId"); 
      x.MapRightKey("BId"); 
      x.ToTable("ABs"); 
     }); 

    modelBuilder.Entity<B>() 
     .HasRequired(b => b.ObjectA) // or HasOptional 
     .WithMany() 
     .WillCascadeOnDelete(false); // not sure if necessary, you can try it 
             // without if you want cascading delete 
} 
+0

Grazie per la tua risposta! Tuttavia, per entrambe le soluzioni ottengo la seguente eccezione: 'Si è verificata una prima eccezione di tipo 'System.InvalidOperationException' in EntityFramework.DLL' – Marthijn

+0

Correzione: la soluzione fluente funziona (errore mio ..), quindi grazie per questo! Qualche idea sul perché "InverseProperty' generi un'eccezione? – Marthijn

+0

@Henkie: l'applicazione si blocca davvero? "La prima eccezione casuale" di solito non è un problema e viene mostrata solo nella finestra di output del debugger. Normalmente può essere ignorato. – Slauma

0

Se la tabella B ha chiave esterna alla tabella A, allora la classe B ha proprietà di navigazione di A e A hanno proprietà di navigazione a ICollection<A>.
Se la tabella B ha molti a molti relazione con la tabella A quindi di classe A deve avere ICollection<B> e la classe B deve avere ICollection<A>.

Prova che, forse questo chiarirà la vostra richiesta dalla EF.

+0

Ho dimenticato di aggiungere l'ICollection alla classe 'A' per i molti-a-molti nel mio esempio di codice. È riparato ora. – Marthijn

+0

@Henkie: Felice di poter aiutare :) – Naor

Problemi correlati