1

我有兩個表,HOTEL和OWNER。 都有和標識列,但其中一個具有所需的其他表的外鍵。EF 5.0不回滾計算出的標識值的交易

我需要在同一時間事務性地添加兩個記錄,但是如果在主表上插入失敗,我需要回滾爲輔助表寫入記錄的事務。

據我所知,我需要.SaveChanges()從輔助表中獲取自動生成的ID,但這也似乎是提交事務。

有沒有其他方式做到這一點?

public class HOTEL 
{ 
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public Int64 HOTEL_ID { get; set; } 

    public string blah1 { get; set; } 

    public string blah2 { get; set; } 
} 

public class OWNER 
{ 
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public Int64 OWNER_ID { get; set; } 

    public string blah3 { get; set; } 

    public string blah4 { get; set; } 

    public Int64 HOTEL_ID { get; set; } 

    [ForeignKey("HOTEL_ID")] 
    public virtual HOTEL HOTEL { get; set; } 
} 

...

public class MyContext : DbContext 
{ 
    public MyContext() : base() { } 
    public MyContext(string connectionString) : base(connectionString) { } 

    public DbSet<HOTEL> HOTELs { get; set; } 
    public DbSet<OWNER> OWNERs { get; set; } 

    public ObjectContext ObjectContext 
    { 
     get 
     { 
      return (this as IObjectContextAdapter).ObjectContext; 
     } 
    } 
} 

...

Int64 ret = 0; 

// Suppress required for DB2. 
using (var transaction = new TransactionScope(TransactionScopeOption.Suppress)) 
{ 
    try 
    { 
     using (var context = new MyContext()) 
     { 
      var secondaryEntity = new HOTEL(); 
      context.HOTELs.Add(secondaryEntity); 

      // This appears to commit the changes in the trasaction. 
      context.SaveChanges(); 

      primaryEntity.HOTEL_ID = secondaryEntity.HOTEL_ID; 

      context.OWNERs.Attach(primaryEntity); 
      context.Entry(primaryEntity).State = primaryEntity.OWNER_ID == 0 ? EntityState.Added : EntityState.Modified; 

      context.SaveChanges(); 
      ret = primaryEntity.OWNER_ID; 
     } 
    } 
    catch (Exception ex) 
    { 
     // Deal with errors. 
    } 

    if (ret != 0) 
    { 
     transaction.Complete(); 
    } 
} 

return ret; 
+0

你爲什麼不添加的所有實體到上下文並保存更改? SaveChanges將所有事物保存在事務中,這樣既可以保存實體,也可以不保存。 – Pawel 2013-03-27 20:30:23

+0

@Pawel如果未調用SaveChanges(),則不會創建數據庫自動生成的標識。 – midspace 2013-03-27 22:45:50

+0

我想說的是,你正確設置引用,你不需要設置外鍵(反正你不會知道這些值) - 保存批次時,它們應該爲你設置..。 – Pawel 2013-03-27 22:52:52

回答

0

在工作之後,事實證明,真正的問題是,因爲我用的是TransactionScopeOption.Suppress。 我正在使用它,因爲它似乎沒有與任何其他選項一起使用。這是因爲我正在使用Client 9.7 Fixpack 4. 使用任何其他TransactionScopeOption時,它返回以下錯誤。

ERROR [57016] [IBM][DB2/NT64] SQL0668N Operation not allowed for reason code "7" on table "DB2ADMIN.HOTEL". 

深入研究,這是因爲DB2並未參與TransactionScope。 在看過關於這個問題的其他幾個問題之後,通過以下步驟解決了問題。在MSDTC性能

  1. 啓用

    • XA交易開始,運行「DCOMCNFG」啓動組件服務管理控制檯。
    • 導航樹到「組件服務>計算機>我的電腦>分佈式事務處理協調器>本地DTC」。
    • 在「本地DTC」上,右鍵單擊並選擇屬性。
    • 選擇「安全」選項卡。
    • 選中「啓用XA事務」選項卡。
    • 按OK。
  • 如果我繼續使用DB2 9.7 FP4,我需要的註冊表項HKLM \ SOFTWARE \微軟\ MSDTC \ XADLL下輸入一個值名爲 'DB2CLI.DLL' 和值「C:\ Program Files文件\ IBM \ SQLLIB \ BIN \ dB2CLI.DLL」

    [HKEY_LOCAL_MACHINE \ SOFTWARE \微軟\ MSDTC \ XADLL] 「dB2CLI.DLL」= 「C:\ Program Files文件\ IBM \ SQLLIB \ BIN \ db2cli.dll」

  • 或者(從註冊表項),至少安裝DB2 Client 9.7 FP6。

  • 重新啓動計算機。
  • 在處理TransactionScope之前,請勿關閉連接。
  • 最終的代碼是這樣的。

    Int64 ret = 0; 
    
    using (var transaction = new TransactionScope(TransactionScopeOption.Required)) 
    { 
        try 
        { 
         using (var context = new MyContext()) 
         { 
          var secondaryEntity = new HOTEL(); 
          context.HOTELs.Add(secondaryEntity); 
    
          // SaveChanges(bool) has been depricated, use SaveOptions. 
          // SaveChanges is required to generate the autogenerated Identity HOTEL_ID. 
          context.ObjectContext.SaveChanges(SaveOptions.AcceptAllChangesAfterSave); 
          primaryEntity.HOTEL_ID = secondaryEntity.HOTEL_ID; 
    
          context.OWNERs.Attach(primaryEntity); 
          context.Entry(primaryEntity).State = primaryEntity.OWNER_ID == 0 ? EntityState.Added : EntityState.Modified; 
    
          context.ObjectContext.SaveChanges(SaveOptions.AcceptAllChangesAfterSave); 
          ret = primaryEntity.OWNER_ID; 
    
          if (ret != 0) 
          { 
           transaction.Complete(); 
           context.ObjectContext.AcceptAllChanges(); 
          } 
         } 
        } 
        catch (Exception ex) 
        { 
         // Deal with errors. 
        } 
    } 
    
    return ret; 
    

    閱讀從@Pawel意見後一種替代,這將產生secondaryEntity的ID,並自動將其附加到primaryEntity,而無需手動分配值:

    Int64 ret = 0; 
    
    using (var transaction = new TransactionScope(TransactionScopeOption.Required)) 
    { 
        try 
        { 
         using (var context = new MyContext()) 
         { 
          var secondaryEntity = new HOTEL(); 
          primaryEntity.HOTEL = secondaryEntity; 
    
          context.HOTELs.Add(secondaryEntity); 
          context.OWNERs.Attach(primaryEntity); 
          context.Entry(primaryEntity).State = primaryEntity.OWNER_ID == 0 ? EntityState.Added : EntityState.Modified; 
    
          context.SaveChanges(); 
          ret = primaryEntity.OWNER_ID; 
    
          // TransactionScopeOption.Required can still used in case something 
          // goes wrong with additional processing at this point. 
    
          if (ret != 0) 
          { 
           transaction.Complete(); 
          } 
         } 
        } 
        catch (Exception ex) 
        { 
         // Deal with errors. 
        } 
    } 
    
    return ret;