2013-03-29 7 views
6

Korzystanie z .NET 4, MVC 4, Entity Framework 5, SQL Server;Jak wstawić klucz obcy, który jeszcze nie istnieje w Entity Framework?

Chcę wstawić do jednej transakcji nowy rekord Nagłówek i kilka nowych rekordów HeaderData, z których wszystkie mają klucz obcy do wstawionego rekordu nagłówka. Rekord nagłówka ma klucz główny 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? 

Więc problemem jest to, że nie wiem, co nagłówek rekord ID będzie umieścić w polu FK rekordy danych, aż po jego popełnienia. A może ja? Po prostu odwołanie się do h.ID, zanim SaveChanges/Commit nie zadziałało, zwróciło 0.

Opcje: 1) Czy powinienem najpierw zatwierdzić rekord nagłówka, uzyskać PK, a następnie zapełnić modele rekordów danych i zatwierdzić je oddzielnie? Może w takim przypadku wykonać wycofanie nagłówka, brzmi jak mniej niż optymalny sposób, aby to zrobić.

2) Czy zamiast tego powinienem użyć identyfikatora GUID PK lub podobnego, gdzie go utworzę tutaj w aplikacji? Jest to jedyne miejsce, w którym można dodać zapisy.

3) Czy istnieje gładki sposób w Entity Framework (tymczasowy EntityKey może?), I czy powinien on wstawiać transakcje, tak aby automatycznie wstawiał właściwy identyfikator nagłówka w rekordach "pola FK? Wydaje się to możliwe do EF, ale nie mogłem dokładnie znaleźć odniesienia do niego.

Odpowiedz

7

Jeśli Header i HeaderData związane są za pomocą klucza obcego (jeden do wielu) związku trzeba mieć kolekcję nawigację Header.HeaderDatas (typu ICollection<HeaderData> lub innego typu kolekcji) w Header lub odniesienie nawigację HeaderData.Header (typu Header) w HeaderData lub nawet w obu.

W obu przypadkach lepiej jest budować relacje z wykorzystaniem tych właściwości nawigacyjnych:

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

Lub:

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

Nie trzeba ustawić FK bezpośrednio. EF poprawnie "przetłumaczy" ustawione właściwości nawigacji na niezbędne wartości kluczy obcych dla tabel bazy danych.

+0

To jest dokładnie to, dziękuję! – Sswan

+0

Czy istnieje jakikolwiek powód, dla którego tabela podrzędna (HeaderData w powyższym przykładzie) generowałaby zero w kolumnie klucza obcego (tj. Tabela PK wrt nagłówka) zamiast generowanego identyfikatora DB (przy użyciu tożsamości (1,1)) .... Innymi słowy, wygenerowane przez DB PK nie są propagowane do tabeli podrzędnej, kiedy nowy rekord jest dodawany do tabeli Rodzic i Dziecko ... proszę o pomoc. – StackticMain

0

Właściwości nawigacyjne nie są konieczne. Użyj transakcji i dwukrotnie wywołaj funkcję SaveChanges(); raz po dodaniu pierwszego elementu i raz na końcu. Ponieważ EF można wykorzystać na wiele różnych sposobów, poniższa próbka może nie być dokładnym rozwiązaniem dla danej sytuacji, ale powinna prowadzić do sedna sprawy.

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