Question

I'm using transaction in my c# code. In TransactionScope I log almost everything to make sure everything is done in testing. But if something happens in transaction and it goes to catch my log class doesn't write anything. In localhost I change TransactionScopeOption to "Suppress" because database is not in my server. When I'm debugging everything is ok, I can write to log file.But when I change to "Required" if it gave an error, it canceled everything as usual but it also can't write first log. How can I fix this?

My code sample:

TransactionOptions tr = new TransactionOptions();
tr.IsolationLevel = System.Transactions.IsolationLevel.Serializable;
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, tr, System.Transactions.EnterpriseServicesInteropOption.Automatic))//System.TimeSpan.MaxValue))
{
    bool status = false;
    // this log must be written anyway but it won't when it gives an error.
    LogProcess log = new LogProcess(Ssession["DBNAME"], new Guid(), "Transaction begun.");
    try
    {
        // process 1
        // process 2
        .
        .
        .
        if(status)
        {
            LogProcess log1 = new LogProcess(Ssession["DBNAME"], new Guid(), "Transaction will complete soon.");
            scope.Complete();
            LogProcess log1 = new LogProcess(Ssession["DBNAME"], new Guid(), "Transaction completed.");
        }
        else
        {
            LogProcess log2 = new LogProcess(Ssession["DBNAME"], new Guid(), "Transaction will dispose.");
            scope.Dispose();
            LogProcess log2 = new LogProcess(Ssession["DBNAME"], new Guid(), "Transaction disposed.");
        }

    }
    catch(Exception ex)
    {
        LogProcess log1 = new LogProcess(Ssession["DBNAME"], new Guid(), "Transaction exception. Error: " + ex.Message);
        scope.Dispose();
    }
}
Was it helpful?

Solution

First of all, I find the structure of your code sample quite confusing. When using a TransactionScope, you typically don't write statements after scope.Complete() or scope.Dispose() have been called. Moreover, the using statement disposes the scope for you, so IMHO you should not call scope.Dispose() explicitly if you can help it. In other words: try to refactor your code so that it looks something like this:

private void PerformTransactionalOperation()
{
    Log.Write("Starting operation.");        

    try
    {
        using (var scope = CreateTransactionScope())
        {
            if (PerformTransactionalOperationCore())
            {
                Log.Write("Committing...");
                scope.Complete();
                Log.Write("Committed");
            }
            else
            {
                Log.Write("Operation aborted.");
            }
        }
    }
    catch (Exception exception)
    {
        Log.Write("Operation failed: " + exception.Message);
        throw;
    }
}

private bool PerformTransactionalOperationCore()
{
    // Perform operations and return status...
}

Secondly, the transaction-scope was specifically designed to complete only when no exception was raised, so your design might be improved by letting the PerformTransactionalOperationCore throw a specific exception instead of returning false.

Then on the logging matter: make sure your logging framework does not participate in the same transaction as the operation your are logging about. You can do this by always logging within a TransactionScope with option Suppress (that is, when you are logging to a resource that is transaction-aware in the first place, like a database). And by the way, why are you writing log-statements by instantiating new LogProcess-instances?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top