0

我有一個方法從它調用Web Api。數據被插入到多個表中,因此添加了事務。以下是代碼佈局。管理跨多種方法和網絡調用的交易

{ 

    TransactionObject objTransaction = new TransactionObject(); 
    try 
    { 

    //Create Order 
    order.insert(objTransaction); 

    //Create Delivery 
    address.insert(objTransaction); 

    //Call Generic Web API method to calculate TAX. 
    using (HttpClient client = new HttpClient()) 
     { 
      client.BaseAddress = new Uri("http://localhost:85766/"); 
      client.DefaultRequestHeaders.Accept.Clear(); 
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 
      var response = client.PostAsJsonAsync("api/Tax/UpdateTax", order).Result; 

      if (response.IsSuccessStatusCode) 
      {  
       //Commit transactio  
       objTransaction.EndTransaction(true);            
      } 
      else 
      { 
       //Commit transactio 
       objTransaction.EndTransaction(false); 
      } 

     } 
    } 
} 
catch(Exception ex) 
{ 
    //Commit transactio 
    objTransaction.EndTransaction(false); 
} 

此方法調用web api來執行另一個數據庫操作。 Web Api下面是一個稱爲form multiple places的通用方法。

 [HttpPost] 
     public HttpResponseMessage UpdateTax(Order order) 
     {    
      TransactionObject objTransaction = new TransactionObject();    
      try 
      { 

       //DO calculation logic 
       . 
       . 
       //Insert Invoice 
       invoice.insert(objTransaction); 
       objTransaction.EndTransaction(true); 
       return Request.CreateResponse(HttpStatusCode.Created, "Invoice data added successfully"); 
      } 
      catch (Exception ex) 
      { 
       objTransaction.EndTransaction(false); 
       return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Erorr while adding invoice"); 
      } 

     } 

現在,如果在調用Web API之前發生異常,則所有事務都將被回滾。沒事。 Web Api中發生異常也是如此。

如果以後網絡API得到成功執行,而在main方法將事務會發生異常,如何處理它,我的意思是在下面的代碼

if (response.IsSuccessStatusCode) 
    {  
    //Commit transactio   
     objTransaction.EndTransaction(true);            
    } 

有沒有更好的辦法來處理邏輯是什麼?

+0

一個選項是使用['TransactionScope'] (https://msdn.microsoft.com/en-us/library/system.transactions.transactionscope(v=vs.110).aspx)與環境事務選項。 – kayess

+0

@ kayess:環境事務選項在網絡調用中不起作用。我發佈了一個答案,在網絡上傳輸事務ID。如果您有任何問題,請提供反饋,謝謝 –

回答

1

您當前的實現不保證原子,當存在UpdateTax方法完成(網絡問題或其他)之後的錯誤,所以你的主要方法接收甚至你的數據已經保存了錯誤考慮的情況。

您可以使用TransactionScope創建分佈式事務來改善它。

爲了使分佈式事務正常工作,您需要打開分佈式事務處理協調器(DTC):https://msdn.microsoft.com/en-us/library/dd327979.aspx。我還假設你的web api應用程序與你的主應用程序在同一個域網絡中。

對於您的情況,您可以創建一個環境事務,以在應用程序邊界內的方法調用之間流動事務,如@kayess在其評論中所建議的。但環境事務不能跨網絡調用,你必須爲此實現額外的邏輯。

這個想法是流程你的交易ID到你的web api應用程序,因此它可以參與你的主應用程序的同一個事務。下面是你的主應用程序示例代碼:

using (var scope = new TransactionScope()) //create ambient transaction by default. 
    { 
     //Create Order (insert Order using your local method) 

     //Create Delivery (insert Delivery using your local method) 

     using (var client = new HttpClient()) 
     { 
      client.BaseAddress = new Uri("http://localhost:85766/"); 
      client.DefaultRequestHeaders.Accept.Clear(); 
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 
      //Send current transaction id as header in the http request 
      var token = TransactionInterop.GetTransmitterPropagationToken(Transaction.Current); 
      //You could improve it by creating a HttpRequestMessage, here is just to demonstrate the idea. 
      client.DefaultRequestHeaders.Add("TransactionToken", Convert.ToBase64String(token));  

      var response = client.PostAsJsonAsync("api/Tax/UpdateTax", order).Result; 
      //Do some more stuff 
      .. 

      //If there is no exception and no error from the response 
      if (response.IsSuccessStatusCode) { 
       scope.Complete(); //Calling this let the transaction manager commit the transaction, without this call, the transaction is rolled back   
      } 
     } 
    } 

你的網頁API方法:

[HttpPost] 
    public HttpResponseMessage UpdateTax(Order order) 
    {    
     try 
     { 
      //Retrieve the transaction id in the header 
      var values = Request.Headers.GetValues("TransactionToken"); 
      if (values != null && values.Any()) 
      { 
       byte[] transactionToken = Convert.FromBase64String(values.FirstOrDefault()); 
       var transaction = TransactionInterop.GetTransactionFromTransmitterPropagationToken(transactionToken); 
       //Enlist in the same transaction 
       using(var transactionScope = new TransactionScope(transaction) 
       { 
        //DO calculation logic 
        . 
        . 
        //Insert Invoice 

        transactionScope.Complete(); 
       } 
      } 
      return Request.CreateResponse(HttpStatusCode.Created, "Invoice data added successfully"); 
     } 
     catch (Exception ex) 
     { 
      return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Erorr while adding invoice"); 
     } 

    } 

注意

請幫助調整你的代碼這個工作。我只是證明了這個想法(沒有檢查null,沒有調整舊的數據庫方法與TransactionScope一起工作,提取到ActionMethod以獲得更好的代碼可重用性,...)