3

I have an asp.net web api where we are starting a transaction using TransactionScope (only for POST,PUT and DELETE but not for GET) in OnActionExecuting of a global action filter and then completing or rolling it back in OnActionExecuted. Recently we decided to make a change so that we can add SqlAzureExecutionStrategy for at least GET calls (since it only works when there is no user-initiated transaction) so that transient failures can be handled for data fetch. I followed the article here and implemented the same thing in our application.below is the code.

Created a new DB configuration class

public class AzureDbConfiguration : DbConfiguration
{
    public const string CallContextKey = "SuspendExecutionStrategy";
    public AzureDbConfiguration()
    {
        this.SetExecutionStrategy("System.Data.SqlClient", () => SuspendExecutionStrategy
          ? (IDbExecutionStrategy)new DefaultExecutionStrategy()
          : new SqlAzureExecutionStrategy());   
    }

    public static bool SuspendExecutionStrategy
    {
        get
        {
            return (bool?)CallContext.LogicalGetData(CallContextKey) ?? false;
        }
        set
        {
            CallContext.LogicalSetData(CallContextKey, value);
        }
    }


}

Setting suspendExecutionStrategy to true whenever we need to start the transaction.

public override void OnActionExecuting(HttpActionContext actionContext)
    {

        //checks if it is not a GET call
        if (RequiresTransactionInitiation(actionContext))
        {

           AzureDbConfiguration.SuspendExecutionStrategy = true;

            var transactionCompleter = new TransactionCompleter(GetDependencyScope(actionContext));
            transactionCompleter.UnitOfWork.StartTransaction(System.Data.IsolationLevel.ReadCommitted);

        }
        base.OnActionExecuting(actionContext);

    }

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        //checks if it is not a GET call
        if (RequiresTransactionCompletion(actionExecutedContext))
        {
            var transactionCompleter = new TransactionCompleter(GetDependencyScope(actionExecutedContext));
            if (actionExecutedContext.Exception == null)
            {

                transactionCompleter.Complete();
                AddEventsToResponseHeader(transactionCompleter.MessageTransactions.OfType<IEventTransaction>(), actionExecutedContext.Response);
            }
            else
            {
                transactionCompleter.Rollback();
            }
        //*********** line is added to just test the value of the property .This always return false.**********
         var testValue = AzureDbConfiguration.SuspendExecutionStrategy;
        }
        base.OnActionExecuted(actionExecutedContext);
    }

Problem is that the value of SuspendExecutionStrategy is set correctly in OnActionExecuting but moment the code enters the Controller action or OnActionExecuting it is always false.

I did some investigation and realized that the execution context itself changes when we move from the filter to controller action. So if in OnActionExecuting I check Thread.CurrentThread.ExecutionContext.ToStringJson() I see the value of SuspendExecutionStrategy is available but if I check the same in controller action it is not available and the strange part is everything else in the execution context is still available.

Value of Thread.CurrentThread.ExecutionContext.ToStringJson() in OnActionExecuting

        {
      "LogicalCallContext": {
        "E2ETrace.ActivityID": "80000013-0003-fd00-b63f-84710c7967bb",
        "ApplicationInsights.OwinExtensions.OperationIdContext": "1c218be6-1fbb-44bf-b994-4db84115b5a3",
        "ApplicationInsights.OwinExtensions.OperationParentIdContext": "72d35ac6-a394-4205-8452-a95b36f8857a",
        "SuspendExecutionStrategy" : True
    })

Value of Thread.CurrentThread.ExecutionContext.ToStringJson() in Controller Action and OnActionExecuted

{
      "LogicalCallContext": {
        "E2ETrace.ActivityID": "80000013-0003-fd00-b63f-84710c7967bb",
        "ApplicationInsights.OwinExtensions.OperationIdContext": "1c218be6-1fbb-44bf-b994-4db84115b5a3",
        "ApplicationInsights.OwinExtensions.OperationParentIdContext": "72d35ac6-a394-4205-8452-a95b36f8857a",
    })

So my questions is how is that execution context changes from Action filter to controller action and how come all other values in the execution context are still retained.

Jags
  • 772
  • 7
  • 17

0 Answers0