我有一个从它调用Web Api的方法。数据被插入到多个表中,因此添加了事务。下面是代码布局。
[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下面是一个称为多个地方的通用方法。
if (response.IsSuccessStatusCode)
{
//Commit transactio
objTransaction.EndTransaction(true);
}
现在,如果在调用Web API之前发生异常,那么将回滚所有事务。没事。如果在Web Api中发生异常,则相同。
如果Web API成功执行后会发生异常,在main方法中提交事务时,如何处理它,我的意思是在下面的代码中
(?<preandconjunct>(?:\b([Ss]ubsection|[Ss]ection|[Aa]rticle) +)(?<conjunct>(?:(?<level>(?:(?:[IVXivx]{1,5}(?![A-Z]))|(?:[A-Z]{1,2}(?![A-Z]))|(?:[0-9]+)))|(?<level>\((?:(?:[IVXivx]{1,5}(?![A-Z]))|(?:[A-Z]{1,2}(?![A-Z]))|(?:(?!in|or|if|of|to|as|at|it|no|an)[a-z]{1,2}(?![a-z]))|(?:[0-9]+))\))|(?<level>[\.-](?:(?:[IVXivx]{1,5}(?![A-Z]))|(?:[A-Z]{1,2}(?![A-Z]))|(?:[0-9]+))))+)(?=$|[ ,;.)]))
有没有更好的方法来处理逻辑?
答案 0 :(得分:1)
您当前的实现不保证原子性,考虑UpdateTax
方法完成后出现错误(网络问题或其他)的情况,因此即使您的数据已保存,您的main方法也会收到错误。< / p>
您可以使用TransactionScope创建分布式事务来改进它。
为了使分布式事务起作用,您需要打开分布式事务处理协调器(DTC):https://msdn.microsoft.com/en-us/library/dd327979.aspx。我还假设您的web api应用程序位于主应用程序的同一域网络中。
在您的情况下,您可以创建环境事务以在应用程序边界内的方法调用之间传递事务,如@kayess在其注释中所建议的那样。但是环境事务在网络调用中不起作用,你必须为此实现额外的逻辑。
我们的想法是将您的交易ID 流程发送到您的网络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以获得更好的代码可重用性,......)