2013-03-29 49 views
6

使用.NET 4,MVC 4,實體框架5,SQL Server;如何使用實體框架中尚不存在的外鍵插入?

我想在一個事務中插入一個新的Header記錄和幾個新的HeaderData記錄,這些記錄都有一個插入Header記錄的外鍵。標題記錄具有標識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? 

所以問題是,我不知道什麼標題記錄ID將被放入數據記錄的FK字段,直到它提交後。或者我?只是在SaveChanges/Commit沒有工作之前引用h.ID,它返回0.

選項: 1)我應該只是首先提交標頭記錄,獲取PK使用,然後填充數據記錄模型並提交他們分開?在這種情況下可能不得不回滾標題,聽起來似乎並不是最佳的方式。

2)我應該使用一個GUID PK或類似的,而我在這裏創建它的應用程序?這是唯一可以添加記錄的地方。

3)在實體框架(臨時EntityKey也許?)中是否有一種流暢的方式,並讓它執行事務插入,以便它會自動將正確的標題ID放入數據記錄的FK字段中?這似乎對EF可行,但我無法準確找到它的參考。

回答

7

如果HeaderHeaderData由外鍵(一個一對多)的關係,你應該在Header導航集合Header.HeaderDatas(的ICollection<HeaderData>類型或另一個集合型)或導航引用(的Header型)HeaderData.Header相關在HeaderData甚至兩個。

在這兩種情況下,更好的辦法是使用這些導航屬性建立的關係:

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

或者:

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

您不需要直接設置FK。 EF將正確地將您設置的導航屬性「翻譯」爲數據庫表的必要外鍵值。

+0

就是這樣,謝謝! – Sswan

+0

是否有任何理由爲什麼子表(HeaderData在上面的例子中)將在外鍵列(即PK wrt Header表)中生成零而不是數據庫生成的ID(使用標識(1,1))....換句話說,當新記錄添加到父表和父表時,數據庫生成的PK不會傳播到子表...請幫助。 – StackticMain

0

導航屬性不是必需的。使用事務並調用SaveChanges()兩次;一次在添加第一個實體後,一次在最後。 由於EF可以有多種不同的使用方式,下面的示例可能不是您的具體情況的確切解決方案,但它應該將重點放在首位。

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