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以获得更好的代码可重用性,...)