12

使用實體框架(代碼優先於我的情況),我有一個操作,需要我調用SaveChanges更新數據庫中的一個對象,然後SaveChanges再次更新另一個對象。 (我需要第一個SaveChanges來解決EF無法確定首先更新哪個對象的問題)。EF:如何在事務內部調用SaveChanges兩次?

我試着這樣做:

using (var transaction = new TransactionScope()) 
{ 
    // Do something 

    db.SaveChanges(); 

    // Do something else 

    db.SaveChanges(); 

    tramsaction.Complete(); 
} 

當我運行的是,我得到一個異常在第二SaveChanges電話,稱「基礎提供失敗的開放」。內部異常說我的機器上未啓用MSDTC。

現在,我已經在其他地方看過描述如何啓用MSDTC的帖子,但似乎我還需要啓用網絡訪問等。這聽起來像是完全矯枉過正,因爲沒有其他數據庫參與,讓單獨的其他服務器我不想做一些讓整個應用程序不太安全(或者更慢)的事情。

當然,必須有一個更輕量級的方式來做到這一點(理想情況下沒有MSDTC)?

+0

您在使用SQL 2008的參考System.Transactions的?根據您的實際邏輯,您可以打開多個連接而不升級。當DTC被調用時,這是一個很好的帖子:[事務範圍自動升級到MSDTC](http:// stackoverflow。com/questions/1690892/transactionscope -automation-escalating-to-msdtc-on-some-machines) –

+0

如果你想在一個事務中做所有事情,那麼保存所有事情或保存所有事情有什麼區別?在這兩種情況下,一切都將被保存或沒有保存,所以我沒有看到多個保存的任何優勢。根據以下評論 - 在Sql Server 2005中打開事務中的多個連接(即使源相同)會導致事務被提升爲分佈式事務。這在Sql Server 2008中得到了改進,您可以在trx中打開多個連接到同一個數據源而不會導致升級 – Pawel

+0

@markoreta:我正在使用SQL Server 2012.我沒有(或想要)多個連接,我真的很想使用MSDTC! –

回答

6

這可能是由於您的交易中使用了兩個不同的連接。嘗試手動控制您的操作連接:

var objectContext = ((IObjectContextAdapter)db).ObjectContext; 

try { 
    object.Context.Connection.Open(); 
    using (var transaction = new TransactionScope()) { 
     // Do something 

     db.SaveChanges(); 

     // Do something else 

     db.SaveChanges(); 

     transaction.Complete(); 
    } 
} finally { 
    objectContext.Connection.Close(); 
} 
+0

如果它在相同的DbContext中,則EF6.0具有context.Database.BeginTransaction()。但是,如果在不同的DBContext中進行操作,這會很好地工作。 –

7

通過調用的SaveChanges()如你是導致該數據被保存到數據庫和EF一下剛剛所做的更改給忘了。

訣竅是使用SaveChanges(false),以便將更改持久保存到數據庫,但EF不會忘記它所做的更改,從而使記錄/重試成爲可能。

 var scope = new TransactionScope(
      TransactionScopeOption.RequiresNew, 
      new TransactionOptions() { IsolationLevel = IsolationLevel.Serializable } 
     ); 

     using (scope) 
     { 
      Entities context1 = new Entities(); 
      // Do Stuff 
      context1.SaveChanges(false); 

      Entities context2 = new Entities(); 
      // Do Stuff 
      context2.SaveChanges(false); 

      scope.Complete(); 
      context1.AcceptAllChanges(); 
      context2.AcceptAllChanges(); 
     } 

P.S.只要您在transactionscope內部打開了多個連接,它就會升級爲DTC。

+0

謝謝。這聽起來非常合理,但在我的情況下,如果出現錯誤,我不關心本地情況的狀態,因爲在那種情況下,我將會轟炸並摧毀環境。不過,很高興知道。 –

+0

如果第一次更新操作正常但第二次更新操作失敗,那麼數據庫中的數據是否一致? –

+0

由於整個事情都包裝在TransactionScope中,所以我希望如此! –

11

我知道這是一種遲到的答案,但我發現它是有用的分享。現在

EF6它更容易使用dbContext.Database.BeginTransaction()

像這樣acheeve這樣的:

using (var context = new BloggingContext()) 
{ 
    using (var dbContextTransaction = context.Database.BeginTransaction()) 
    { 
     try 
     { 
      // do your changes 
      context.SaveChanges(); 

      // do another changes 
      context.SaveChanges(); 

      dbContextTransaction.Commit(); 
     } 
     catch (Exception) 
     { 
      dbContextTransaction.Rollback(); 
     } 
    } 
} 

更多信息,看看this

再次它在EF6勇往直前

+0

謝謝,這更像它! –

+0

歡迎您:) –

+0

您應該使用catch塊做更多的事情,而不僅僅是回滾併吞下異常。 Log,rethrow,... –

1

對於決賽上面選擇的答案,有一個錯字。下面更正行:

objectContext.Connection.Open(); 

另外,需要添加System.Data.Entity.Infrastructure和