2

I have a very ugly problem with a code made by a co worker. The action is within a TransactionScope. First a database insert is performed:

var billingRecord = new Billing
                {
                    ACCOUNT_ID = AccountId,
                    AMOUNT = Amount,

                };
_ObjectContext.AddToBilling(billingRecord);
_ObjectContext.SaveChanges(SaveOptions.None)

Then a web service call is performed:

var webServiceCallResult = Request(params);

if (webServiceCallResult.Result == 1)
{
    success = true;
}
else
{
    success = false;   
}

If the web service call is ok, the transaction is completed in a finally block:

finally
{
    if (success)
    {
        tran.Complete();
        _ObjectContext.AcceptAllChanges();
        _Logger.Info(String.Format("Transaction completed"));
    } 
    else
    {
        _Logger.Info(String.Format("Transaction uncompleted"));
    }
}

The problem is that for some reason, some records are not stored in the database. I tested a lot of transactions but that never happen to me in development environment, but sometimes happens in production environment. When I say "sometimes" it's because this situation is very unusual or rare.

Looking in the log file, I can see the message:

Transaction completed

and no exceptions displayed, so the web service call is good and the transaction was completed but the record was not inserted to table.

I know that is not necessary to create a TransactionScope because there is a only a insert and no additional database operations are needed. The object context of EF is created like a global var in the class and is never disposed , that is a bad practice but as far as I have knowledge the ObjectContext will be destroyed by the garbage collector, so I think that is not what causes the problem.

I read a lot about transactions and the correct way to use a Entity Framework ObjectContext and the methods SaveChanges() and AcceptAllChanges() and even the code is not using the best practices that should work. I don't want only refactor the code, I would like to know the real reason of the problem.

I would appreciate your help.

I am using:

  • ASP.NET MVC 3,
  • Entity Framework 5.0,
  • Ninject,
  • DevExpress

Here is the the complete class:

    public class Implementation : IExecute
    {
        private readonly Logger _Logger;
        private readonly ExampleEntities _ObjectContext = new ExampleEntities();

        public TopUpExecuteImplementation()
        {            
            _Logger = LogManager.GetLogger("Logger");
        }



        public Response perfomOperation(String account, String amount)
        {
            var success = false;

            using (var tran = new System.Transactions.TransactionScope())
            {
                try
                {
                    var accountRecord =
                        _ObjectContext.Accounts.First(
                            p => p.Account.Equals(account, StringComparison.InvariantCultureIgnoreCase));

                    var billingRecord = new Billing
                    {
                        ACCOUNT = account,
                        AMOUNT = amount,

                    };
                    _ObjectContext.AddToBillings(billingRecord);
                    _ObjectContext.SaveChanges(SaveOptions.None);

                    var webServiceCallResult = Request(account,amount);
                    _Logger.Info(String.Format("Request  Result {0} ", webServiceCallResult.Result));

                    if (webServiceCallResult.Result == 0)
                    {
                        success = false;
                    }
                    else
                    {
                        if ((String.IsNullOrEmpty(webServiceCallResult.statusCode) == false) &&
                            (webServiceCallResult.statusCode.Equals("Success",
                                StringComparison.InvariantCultureIgnoreCase)))
                        {
                            success = true;

                        }
                        else
                        {
                          success = false;

                        }
                    }
                }
                catch (OptimisticConcurrencyException ex)
                {
                    _Logger.Info(String.Format("Exception type {0} Exception {1} Inner Exception {2} ",
                        ex.GetType().ToString(), ex.Message,
                        ex.InnerException != null ? ex.InnerException.Message : String.Empty));
                    _ObjectContext.SaveChanges();
                    success = true;

                }
                catch (Exception e)
                {
                    _Logger.Info(String.Format("Exception type {0} Exception {1} Inner Exception {2} ",
                        e.GetType().ToString(), e.Message,
                        e.InnerException != null ? e.InnerException.Message : String.Empty));
                    success = false;
                }
                finally
                {
                    if (success)
                    {
                        tran.Complete();
                        _ObjectContext.AcceptAllChanges();
                        _Logger.Info(String.Format("Transaction completed"));
                    }
                    else
                        _Logger.Info(String.Format("Transaction uncompleted"));
                }
            }
            return returnValue;
        }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
herbergt
  • 159
  • 1
  • 1
  • 8
  • Have you tried explicitly calling the commit function in the finally statement. The commit work doesn't actually happen in the complete function, it just sets the state of the transactions to be ready to commit. Commit work occurs at the final brace of the using statement. Also See this link it appears to be related http://stackoverflow.com/questions/11776945/is-it-a-good-approach-to-call-return-inside-using-statement – Bearcat9425 Nov 12 '15 at 20:03
  • Thank you for your answer Marc, transaction.complete() method is called within transaction scope, then _ObjectContext.AcceptAllChanges() is called and the response (returnValue) is returned out of the transaction scope. As I said before, it happens sometimes, not always and I can not replicate. – herbergt Nov 12 '15 at 21:15

0 Answers0