2016-01-22 93 views
1

我正在運行下面的代碼,根據每天早上發送給我們的銀行交易歷史記錄文件更新一些記錄。這是非常基本的東西,但由於某種原因,當我達到最後,dbContext.GetChangeSet()報告「0」的所有行動。Linq2Sql更改跟蹤不起作用

public void ProcessBatchFile(string fileName) 
{ 
    List<string[]> failed = new List<string[]>(); 
    int recCount = 0; 
    DateTime dtStart = DateTime.Now; 
    using (ePermitsDataContext dbContext = new ePermitsDataContext()) 
    { 
     try 
     { 
      // A transaction must be begun before any data is read. 
      dbContext.BeginTransaction(); 
      dbContext.ObjectTrackingEnabled = true; 

      // Load all the records for this batch file. 
      var batchRecords = (from b in dbContext.AmegyDailyFiles 
           where b.FileName == fileName 
           && b.BatchProcessed == false 
           && (b.FailReason == null || b.FailReason.Trim().Length < 1) 
           select b); 

      // Loop through the loaded records 
      int paymentID; 
      foreach (var r in batchRecords) 
      { 
       paymentID = 0; 
       try 
       { 
        // We have to 'parse' the primary key, since it's stored as a string value with leading zero's. 
        if (!int.TryParse(r.TransAct.TrimStart('0'), out paymentID)) 
         throw new Exception("TransAct value is not a valid integer: " + r.TransAct); 

        // Store the parsed, Int32 value in the original record and read the "real" record from the database. 
        r.OrderPaymentID = paymentID; 
        var orderPayment = this.GetOrderPayment(dbContext, paymentID); 

        if (string.IsNullOrWhiteSpace(orderPayment.AuthorizationCode)) 
         // If we haven't processed this payment "Payment Received" do it now. 
         this.PaymentReceived(orderPayment, r.AuthorizationNumber); 

        // Update the PaymentTypeDetailID (type of Credit Card--all other types will return NULL). 
        var paymentTypeDetail = dbContext.PaymentTypes.FirstOrDefault(w => w.PaymentType1 == r.PayType); 
        orderPayment.PaymentTypeDetailID = (paymentTypeDetail != null ? (int?)paymentTypeDetail.PaymentTypeID : null); 

        // Match the batch record as processed. 
        r.BatchProcessed = true; 
        r.BatchProcessedDateTime = DateTime.Now; 
        dbContext.SubmitChanges(); 
       } 
       catch (Exception ex) 
       { 
        // If there's a problem, just record the error message and add it to the "failed" list for logging and notification. 
        if (paymentID > 0) 
         r.OrderPaymentID = paymentID; 
        r.BatchProcessed = false; 
        r.BatchProcessedDateTime = null; 
        r.FailReason = ex.Message; 
        failed.Add(new string[] { r.TransAct, ex.Message }); 
        dbContext.SubmitChanges(); 
       } 
       recCount++; 
      } 

      dbContext.CommitTransaction(); 
     } 
     // Any transaction will already be commited, if the process completed successfully. I just want to make 
     // absolutely certain that there's no chance of leaving a transaction open. 
     finally { dbContext.RollbackTransaction(); } 
    } 

    TimeSpan procTime = DateTime.Now.Subtract(dtStart); 

    // Send an email notification that the processor completed. 
    System.Text.StringBuilder sb = new System.Text.StringBuilder(); 
    sb.AppendFormat("<p>Processed {0} batch records from batch file '{1}'.</p>", recCount, fileName); 
    if (failed.Count > 0) 
    { 
     sb.AppendFormat("<p>The following {0} records failed:</p>", failed.Count); 
     sb.Append("<ul>"); 
     for (int i = 0; i < failed.Count; i++) 
      sb.AppendFormat("<li>{0}: {1}</li>", failed[i][0], failed[i][1]); 
     sb.Append("<ul>"); 
    } 
    sb.AppendFormat("<p>Time taken: {0}:{1}:{2}.{3}</p>", procTime.Hours, procTime.Minutes, procTime.Seconds, procTime.Milliseconds); 
    EMailHelper.SendAdminEmailNotification("Batch Processing Complete", sb.ToString(), true); 
} 

dbContext.BeginTransaction()方法是我添加到DataContext這只是爲了方便使用顯式事務。我相當確信這不是問題,因爲它在應用程序的其他地方被廣泛使用。我們的數據庫設計使得有必要對某些特定的操作使用顯式事務,而對「PaymentReceived」的調用恰好是其中之一。

我已經通過代碼加強,並確認對交易本身的Rollback()方法不叫開始,我還檢查了dbContext.GetChangeSet()調用CommitTransaction()與以前相同的結果發生。

爲了清楚起見,我在下面包括了BeginTransaction(),CommitTransaction()RollbackTransaction()方法體。

/// <summary> 
/// Begins a new explicit transaction on this context. This is useful if you need to perform a call to SubmitChanges multiple times due to "circular" foreign key linkage, but still want to maintain an atomic write. 
/// </summary> 
public void BeginTransaction() 
{ 
    if (this.HasOpenTransaction) 
     return; 

    if (this.Connection.State != System.Data.ConnectionState.Open) 
     this.Connection.Open(); 

    System.Data.Common.DbTransaction trans = this.Connection.BeginTransaction(); 
    this.Transaction = trans; 
    this._openTrans = true; 
} 
/// <summary> 
/// Commits the current transaction (if active) and submits all changes on this context. 
/// </summary> 
public void CommitTransaction() 
{ 
    this.SubmitChanges(); 
    if (this.Transaction != null) 
     this.Transaction.Commit(); 
    this._openTrans = false; 
    this.RollbackTransaction(); // Since the transaction has already been committed, this just disposes and decouples the transaction object itself. 
} 
/// <summary> 
/// Disposes and removes an existing transaction on the this context. This is useful if you want to use the context again after an explicit transaction has been used. 
/// </summary> 
public void RollbackTransaction() 
{ 
    // Kill/Rollback the transaction, as necessary. 
    try 
    { 
     if (this.Transaction != null) 
     { 
      if (this._openTrans) 
       this.Transaction.Rollback(); 
      this.Transaction.Dispose(); 
      this.Transaction = null; 
     } 
     this._openTrans = false; 
    } 
    catch (ObjectDisposedException) { } // If this gets called after the object is disposed, we don't want to let it throw exceptions. 
    catch { throw; } 
} 
+1

'catch(ObjectDisposedException){}'吞噬嚴重的錯誤。修復錯誤。不要隱藏錯誤信息。 – usr

+1

這個錯誤處理被細微地破壞了。如果你使一個實體無效並在寫入時失敗,所有未來的寫入都會失敗,因爲實體被困在上下文中。 – usr

+1

我建議你放棄這些事務助手方法。通常的'使用(var tran = BeginTran()){tran.Commit(); ''是你所需要的。做同樣的事情,但更簡單。 – usr

回答

1

我只是發現了問題:我的DBA沒有把主鍵放在桌上時,他創造了它對於我來說,這樣LinqToSql沒有在實體產生任何的「的PropertyChanged」事件/處理的東西類,這就是爲什麼DataContext不知道正在進行更改。顯然,如果你的表沒有主鍵,Linq2Sql將不會跟蹤對該表的任何更改,這是有道理的,但如果出現某種類型的通知,那將會很好。我確定我的DBA沒有考慮這個問題,因爲這只是一種「跟蹤」文本文件中的哪些行項目已被處理並且不直接與任何其他表格關聯的方式。