2013-04-17 76 views
3

我有以下代碼(EntityContext.Current是的DbContext):實體框架的SaveChanges 5()不回滾嘗試的事務

try { 
    Setting s = new Setting 
    { 
     id = 1, 
     user_id = 0 
    }; 

    EntityContext.Current.Settings.Add(s); 
    EntityContext.Current.SaveChanges(); //this violates a foreign key constraint and therefore throws an exception 
} catch (Exception ex) { 
    ErrorContext.Log(ex); 
} 

我期望(按照this answer)是未決的改變會當他們失敗時會被回滾。但是,在我的錯誤記錄方法中,我發現他們沒有。

Error e = new Error 
{ 
    message = ex.Message 
}; 

EntityContext.Current.Errors.Add(e); 

var pending = ((IObjectContextAdapter)EntityContext.Current).ObjectContext.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added); 
//this contains both my new error entity and my old setting entity which shouldn't be here 

EntityContext.Current.SaveChanges(); 

當我的應用程序嘗試記錄錯誤在數據庫中,它也會嘗試(再一次),以保存無效的外鍵約束,然後導致另外的錯誤日誌記錄的失敗的設置。

這是我第一次遇到EF這種行爲。我嘗試在交易中包裝第一個SaveChanges(),但發生了同樣的問題。

根據this answer我的連接字符串不包含Enlist=false要麼。我很茫然。這是預期的行爲?我怎樣才能防止這種情況發生?

回答

3

這是預期的行爲。 SaveChanges嘗試提交單個數據庫事務中的所有更改。如果失敗,數據庫事務將回滾並且不會寫入數據庫。

但失敗的事務不會改變上下文中實體的狀態。這就是爲什麼你的Setting實體仍處於狀態Added

將您的Error實體附加到相同的上下文中是個問題,因爲您經常無法將其存儲到數據庫中,就像本例中一樣。我會考慮在一個新的上下文中用一個專門的方法寫錯誤日誌,這個方法只會打開一個新的上下文,將Error實體添加到這個上下文並保存更改。然後立即處置它。

+0

我看...非常有趣。我想我也可以簡單地刪除catch塊中的那個實體?那樣當我去寫錯誤(使用相同的上下文)時,它將不再嘗試寫入設置? – Mansfield

+1

@Mansfield:是的,你可以分離實體。這個簡單的例子很容易。但假設你有一個更復雜的場景,並且上下文將包含大量實體,其中包括已添加,已刪除,已修改狀態,並且其中一個會導致違反FK約束。在大多數情況下,很難找到catch塊中有問題的實體並從錯誤中恢復。 – Slauma