I making a few API calls inside the transaction block. From what I know if any of these APIs throw an exception then the transaction should rollback. By rollback I understand that all the tasks that the APIs that successfully executed did is undone. The problem is that even if an API throws an exception the scope.Complete() is reached and the rollback does not take place.
This is my transaction block. The method it is being called in does not have a try catch block
using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
await UpdateAuditPlanL3Async(auditCalender);
await SaveAuditManagementL3(auditMgmt);
await SaveAuditManagementStdDetaiL3(mgmtStdDetails);
await SaveAuditObservationDetailL3(obsdetail);
scope.Complete();
}
The parameters to the awaited methods are lists
This below is a sample code to show the way i am calling the API
public async Task UpdateAuditPlanL3Async(AuditCalendarL3 auditCalendarl3)
{
string url = string.Concat(GlobalDeclaration.APIServerUrl, "UpdateAuditPlanL3?AuditplanId=" + auditCalendarl3.AuditplanId + "&Token=" + GlobalDeclaration.ApiToken);
url = GlobalDeclaration.CleanURL(url);
var myContent = JsonConvert.SerializeObject(auditCalendarl3);
var requestContent = new StringContent(myContent, Encoding.UTF8, "application/json");
var response = await Program.httpClient.PutAsync(url, requestContent);
var responseCode = response.StatusCode;
if (responseCode == HttpStatusCode.OK || responseCode == HttpStatusCode.Created || responseCode == HttpStatusCode.Accepted)
{
GlobalDeclaration.log.Info("UpdateAuditPlanL3 API executed successfully. The response code is " + responseCode);
}
else
{
GlobalDeclaration.exlog.Error("UpdateAuditPlanL3 API failed. The response code is " + responseCode);
throw new Exception("UpdateAuditPlanL3 API execution failed.");
}
}
In my case the below API is throwing an exception. Now i know why the exception is being thrown. I want to know how can i solve the rollback not happening even when the exception is being thrown
private async Task SaveAuditObservationDetailL3(List<AuditObservationDetailL3> adobsdtl)
{
string url = string.Concat(GlobalDeclaration.APIServerUrl, "SaveAuditObservationDetailL3?Token=" + GlobalDeclaration.ApiToken);
url = GlobalDeclaration.CleanURL(url);
var myContent = JsonConvert.SerializeObject(adobsdtl);
var requestContent = new StringContent(myContent, Encoding.UTF8, "application/json");
var response = await Program.httpClient.PostAsync(url, requestContent);
var responseCode = response.StatusCode;
if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.Created || response.StatusCode == HttpStatusCode.Accepted)
{
GlobalDeclaration.log.Info("SaveAuditObservationDetailL3 API executed successfully. The response code is " + responseCode);
MessageBox.Show("Observation Checkpoints created successfully.");
}
else
{
GlobalDeclaration.exlog.Error("SaveAuditObservationDetailL3 API failed. The response code is " + responseCode);
throw new Exception("SaveAuditObservationDetailL3 API failed. The response code is " + responseCode);
}
}
I have read on how to use transactionscope and everywhere it says that the rollback should occur. I also tried using a try catch block inside the transaction scope but to no avail. I even removed the TransactionScopeAsynFlowOption.Enabled and also tried the .Suppress but it always reaches the scope.Complete() even when an exception is thrown.
No, I do not think that is what is happening here. A few more likely options:
SqlConnectionmust be opened inside the ambient transaction in order for it to support this usageGiven that you've shown some of the HTTP API code, I think we can rule out 2, and you seem confident that we can rule out 1, and you have enabled async flow in thr transaction (that was going to be my item zero), so that leaves 3 and 4; what is the thing that you are expecting to rollback? Can we see that, and how confident are you that it supports ambient transactions?
To be clear: random HTTP calls do not support transactions, ambient or explicit. Transactions require lots of coordination, requiring lots of logic and work on both sides; WCF offers some of this, for example; it may also require additional services such as DTC. Mostly, ambient transactions tend to be restricted to SQL work (on a subset of ADO.NET providers), because SQL databases at least usually have a concept of transactions; raw HTTP does not.
(side note: ambient transactions are convenient, but almost always a bad approach, although this is somewhat subjective, or at least contextual)