Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use transactions for different contexts?

In my application I have method looks like this:

public static bool DoLargeOperation()
{
    bool res = true;

    res = res && DoFirstSubOperation();
    res = res && DoSecondSubOperation();
    res = res && DoThirdSubOperation();

    return res;
}

And each of the internal methods looks like this:

public static bool DoFirstSubOperation()
{
    using (var context = new EntityFrameworkContext())
    {
        // data modification.
        context.SaveChanges();
    }
}

For example, DoFirstSubOperation() and DoSecondSubOperation() complete successfully, but DoThirdSubOperation() fails. How do I rollback the changes made by the first two functions?

This approach has not brought results:

using (var transaction = new TransactionScope())
{
    res = res && DoFirstSubOperation();
    res = res && DoSecondSubOperation();
    res = res && DoThirdSubOperation();
}

The only solution I see is to define the context like so:

public static bool DoLargeOperation()
{
    bool res = true;

    using (var context = new EntityFrameworkContext())
    {
        using (var transaction = context.Database.BeginTransaction())
        {

            res = res && DoFirstSubOperation(context);
            res = res && DoSecondSubOperation(context);
            res = res && DoThirdSubOperation(context);
            if (res)
            {
                transaction.Commit();
            }
            else
            {
                transaction.Rollback();
            }
        }
    }

    return res;
}

But is it acceptable to do so? Or is there another solution?

like image 760
Egor Lavrentev Avatar asked Mar 03 '15 04:03

Egor Lavrentev


1 Answers

Yes, that is the correct pattern. Passing the context into the methods allows the methods to be reused in multiple locations because the context and transaction will be managed by the caller.

Although, you will probably want to stop processing subsequent methods once the first one fails. You also probably want to wrap the call to the children in a try/catch so that any exception allows the rollback to occur correctly...

try
{
    res = DoFirstSubOperation(context);
    if (res) 
        res = DoSecondSubOperation(context);
    if (res) 
        res = DoThirdSubOperation(context);    

    if (res)
        transaction.Commit();
    else
        transaction.Rollback();
}
catch
{
    transaction.Rollback();
}

If your child methods already handle exceptions then you can forgo the try/catch.

like image 133
Phil Wright Avatar answered Oct 06 '22 18:10

Phil Wright