2013-03-29 1 views
6

Utilizzo di .NET 4, MVC 4, Entity Framework 5, SQL Server;Come inserire con chiave esterna che non esiste ancora in Entity Framework?

Voglio inserire in una transazione un nuovo record di intestazione e diversi nuovi record HeaderData che hanno tutti una chiave esterna per il record di intestazione inserito. Il record di intestazione ha una chiave primaria Identity int.

Entities.Header h = new Entities.Header(); 

h.Name = name; 
h.Time = DateTime.Now; 
h.Comments = comments; 

db.Headers.Add(h); 
// db.SaveChanges(); // Save changes here? 
// and get ID to use below via h.ID? 

foreach (DataRecord dr in datarecords) // my own custom types here 
{ 
    Entities.HeaderData hd = new Entities.HeaderData(); 
    // hd.header = thisid // ?? this is the FK to Header.ID, its Identity int PK 
    hd.name = dr.name 
    hd.value = dr.value 

    db.HeaderDatas.Add(hd) 
} 

db.SaveChanges(); // or wait to save all here? 

Così problema è che non so cosa l'intestazione ID della notizia sta per essere mettere in campo FK record di dati fino a dopo che si è impegnata. O io? Proprio riferimento h.ID prima che i SaveChanges/Commit non ha funzionato, è tornato 0.

Opzioni: 1) devo solo impegnare il record di intestazione prima, ottenere il PK da utilizzare, quindi popolare i modelli di set di dati e di impegnarsi loro separatamente? In tal caso, potrebbe essere necessario eseguire un rollback dell'intestazione, ma suona come un modo tutt'altro che ottimale.

2) Dovrei utilizzare un GUID PK o simile, invece, dove lo creo qui nell'app? Questo è l'unico posto in cui i record possono essere aggiunti comunque.

3) Esiste un modo intelligente in Entity Framework (EntityKey temporaneo può essere?) E deve fare gli inserimenti di transazione in modo che inserisca automaticamente l'ID di intestazione corretto nei campi FK dei record di dati? Questo sembra fattibile per EF, ma non ho potuto trovare esattamente un riferimento ad esso.

risposta

7

Se Header e HeaderData sono legati da una chiave esterna (uno-a-molti) rapporto si dovrebbe avere una collezione di navigazione Header.HeaderDatas (di tipo ICollection<HeaderData> o un altro tipo di raccolta) in Header o un riferimento di navigazione HeaderData.Header (di tipo Header) in HeaderData o anche entrambi.

In entrambi i casi il modo migliore è quello di costruire il rapporto con le proprietà di navigazione:

Entities.Header h = new Entities.Header(); 
h.HeaderDatas = new List<HeaderData>(); 
// ... 
foreach (DataRecord dr in datarecords) 
{ 
    Entities.HeaderData hd = new Entities.HeaderData(); 
    //... 
    h.HeaderDatas.Add(hd) 
} 
db.Headers.Add(h); 
db.SaveChanges(); 

Oppure:

Entities.Header h = new Entities.Header(); 
// ... 
foreach (DataRecord dr in datarecords) 
{ 
    Entities.HeaderData hd = new Entities.HeaderData(); 
    //... 
    hd.Header = h; 

    db.HeaderDatas.Add(hd); 
} 
db.SaveChanges(); 

Non è necessario impostare direttamente l'FK. EF tradurrà correttamente le proprietà di navigazione che hai impostato nei valori delle chiavi esterne necessarie per le tabelle del database.

+0

Questo è esattamente, grazie! – Sswan

+0

C'è qualche motivo per cui la tabella figlio (HeaderData nell'esempio precedente) genererebbe zero nella colonna chiave esterna (cioè, PK wrt Header table) anziché ID generato da DB (usando identity (1,1)) .... In altre parole, i PK generati dal DB non si stanno propagando alla tabella figlia quando un nuovo record viene aggiunto alla tabella padre e figlio ... per favore aiutatemi. – StackticMain

0

Le proprietà di navigazione non sono necessarie. Utilizzare una transazione e chiamare SaveChanges() due volte; una volta dopo aver aggiunto la prima entità e una volta alla fine. Dato che l'EF può essere usato in molti modi diversi, l'esempio qui sotto potrebbe non essere la soluzione esatta per la tua situazione, ma dovrebbe portare a casa il punto.

using (MyContext context = new MyContext()) 
using (DbContextTransaction txn = context.Database.BeginTransaction()) 
{ 
    try 
    { 
     EntityA a = new EntityA(); 
     a.Foo = "bar"; 
     context.A_DbSet.Add(a); // Assuming the use of the DbSet<T> class 
     context.SaveChanges(); 
     // A.ID is now set, but not committed 

     EntityB b = new EntityB(); 
     b.A_ID = a.ID; 
     b.Foo2 = "bar2"; 
     context.B_DbSet.Add(b); 
     context.SaveChanges(); 
     txn.Commit(); 
    } 
    catch (Exception ex) 
    { 
     txn?.Rollback(); 
    } 
}