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;
        }
}
 
    