2014-07-07 9 views
7

Nei primi modelli di codice EntityFramework, esiste una relazione 1: 1:Una proprietà dipendente in un ReferentialConstraint è associato a un errore colonna store-generato rapporto 1-a-1

public class Child1 
{ 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int Id { get; set; } 

    public string Name { get; set; } 

    public Child2 Child2 { get; set; } 
} 

public class Child2 
{ 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    [ForeignKey("Child1")] 
    public int Id { get; set; } 

    public string Name { get; set; } 

    public Child1 Child1 { get; set; } 
} 

Quando provai a inserire alcuni dati nel database, è generata un'eccezione:

{"A dependent property in a ReferentialConstraint is mapped to a 
    store-generated column. Column: 'Id'."} 

sembra che io non posso usare generato automaticamente ID per Child2, come posso mantenere questa funzione e rendere il rapporto stabilito con successo nel frattempo?

risposta

21

Qui ci sono due problemi, quello ovvio, mostrato nell'eccezione. Quando si definisce una relazione uno-a-uno, l'FK deve essere anche il PK. In questo caso il PK e FK di entrambe le entità sono il campo Id. Il problema mostrato nell'eccezione è che l'FK è generato dal database. Pertanto, se si inserisce uno Child1 con un valore Child2 correlato, EF non ha modo di impostare il valore FK del relativo Child2 perché è generato dal database.

Il secondo problema, che non si è ancora verificato, è che una relazione uno-a-uno è solo una cosa teorica in un database come SQL Server. Se si desidera inserire Child1 che dipende da Child2, è necessario inserire prima Child1 e quindi il relativo Child2. È giusto, ma, ooops, devi anche inserire Child2 prima di inserire Child1, perché Child1 dipende anche da Child2. Quindi, avere una relazione pura uno a uno non è possibile.

per risolvere questo problema è necessario fare due cose:

  1. rendere il rapporto di 1-to-(0 o 1). Cioè devi avere un'entità principale e un'entità dipendente che può o non può esistere. Ciò consentirà di inserire l'entità principale, senza l'entità dipendente, poiché con questa configurazione non è possibile impostare l'entità senza la dipendenza.
  2. il PK principale può essere lasciato come database generato, ma è necessario modificare il PK sull'entità dipendente per non generare DB. Quindi, quando si inserisce l'entità dipendente, il PK, che è anche l'FK, può essere liberamente specificato da EF.

Infine, se ci pensate, una relazione 1-a-1 di solito non ha senso. È possibile utilizzare una singola tabella che contiene tutte le colonne in entrambe le tabelle, poiché quando esiste una riga nella tabella A, deve esistere nella tabella B e viceversa. Quindi avere un solo tavolo ha lo stesso effetto.

Tuttavia, se si vuole ancora utilizzare il rapporto 1 a 1, EF permette di modellare in questo modo:

modelBuilder.Entity<Child1>() 
      .HasRequired(c1 => c1.Child2) 
      .WithRequiredPrincipal(c2 => c2.Child1); 

Si noti che, in questo caso, l'astrazione EF si prende cura per consentire avere una relazione 1 a 1, anche se non può esistere nel DB. Tuttavia, è necessario specificare questa relazione utilizzando ModelBuilder perché è necessario specificare un principal e un lato dipendente. In questo caso il principale è Child1 e il dipendente è Child2. Si noti che è ancora necessario fare attenzione con la regola per i valori generati dal database.

Si noti che questo è modellato nel DB con un singolo FK da Child2 a Child1, e non FK da Child1 a Child2. Quindi, nel DB è una relazione (1) a (0 o 1), come spiegato sopra

public class Child1 
{ 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] // Leave as is 
    public int Id { get; set; } 
    ... 


public class Child2 
{ 
    //[DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    [DatabaseGenerated(DatabaseGeneratedOption.None)] // Not db-generated 
    //[ForeignKey("Child1")] -- specified in the model builder 
    public int Id { get; set; } 
+0

Grazie. Il motivo per cui uso la relazione 1: 1 è che, dobbiamo separare il modello Child1 e Child2 a causa di altri componenti che hanno bisogno di queste due classi. E a proposito, puoi pubblicare le classi di modelli modificate per mostrare quali modifiche devo adattare per i due modelli precedenti, dato che aggiungo la configurazione del builder del modello, ha ancora lo stesso errore. –

+0

Ho aggiornato la mia risposta con il modello modificato. Ricorda che, dato che stai lavorando con una relazione 1 a 1, e 'Child2' dipende da' Child1', il suo 'Id' sarà copiato dal relativo' Child1' quando lo crei. NOTA: se hai solo una tabella, ci sono altri modi per risolvere questo problema: per esempio potresti modificare gli altri componenti per richiedere 'IChild1' e' IChild2' e implementare tali interfacce sull'unica entità esistente, o usare i mapper (AutoMapper , ValueInjecter). Non so come appare la tua app e se questo potrebbe funzionare per te. – JotaBe

+0

Genera nuova eccezione: '{" Impossibile inserire il valore esplicito per la colonna Identity nella tabella 'Child2' quando IDENTITY_INSERT è impostato su OFF. "}', Cosa significa? Il codice completo può essere visto [qui] (https://gist.github.com/JerryBian/8ed6605453adaed579b2). –