1

What is the best way to use(reuse) transactions in Entity Framework 6. Use case: If we have two methods

public decimal Withdraw(string accNum, decimal amount) 
{
    using (var db = new MyDbContext())
    {
        var query = from t in db.MoneyTransactions
                    where t.AccountNumber.Equals(accNum)
                    select t;
        MoneyTransaction mt = query.First();
        mt.Amount -= amount;
        db.SaveChanges();
    }
    return amount;
}

public void Put(string accNum, decimal amount)
{
    using (var db = new MyDbContext())
    {
        var query = from t in db.MoneyTransactions
                    where t.AccountNumber.Equals(accNum)
                    select t;
        MoneyTransaction mt = query.First();
        mt.Amount += amount;
        db.SaveChanges();
     }
}

I want to use this methods separately(e.g. just put money in an account) and also use them in a single transaction:

public void Transfer(string srcAcc, string destAcc, decimal amount) 
{
    Withdraw(srcAcc,amount);
    Put(destAcc,amount);
}
Muath
  • 4,351
  • 12
  • 42
  • 69
Evgeni Dimitrov
  • 21,976
  • 33
  • 120
  • 145

2 Answers2

4

Look into Systems.Transactions assembly and namespace. (Part of the BCL)

Then you can do something like this:

using(var trans = new TransactionScope())
{
    Withdraw(srcAcc, amount);        // No changes needed to these
    Put(destAcc, amount);            //   two methods...

    trans.Complete();
}

System.Transactions will create whats called an ambient transaction. This is a transaction stored on the thread, and any ADO.NET, LINQ-to-SQL and Entity Framework DB operations will use look for this TransactionScope object.

This transaction is either committed or rolled back at the end of the using block. Calling Complete() on the TransactionScope object will trigger a commit in the database, while NOT calling Complete() will roll back the transaction instead.

Arjan Einbu
  • 13,543
  • 2
  • 56
  • 59
  • EF provides transaction mechanisms, no need to use anything else for pure db transactions. – Rodrigo Jul 20 '14 at 19:24
  • @Rodrigo: True, and System.Transactions makes it even easier to use it. Why would you not use System.Transactions for this? – Arjan Einbu Jul 20 '14 at 19:36
  • Any idea how this code could use async/await? If the transaction is stored in the thread, this is quite a problematic scope... – Moti Azu Jul 20 '14 at 20:20
  • 1
    @mot: Haven't tried it, but a quick search turns up this result: http://stackoverflow.com/a/17527759/19594 – Arjan Einbu Jul 20 '14 at 20:43
1

You can use overloaded methods:

public void Put(string accNum, decimal amount, MyDbContext context)
{
    var query = from t in context.MoneyTransactions
                where t.AccountNumber.Equals(accNum)
                select t;
    MoneyTransaction mt = query.First();
    mt.Amount += amount;
    db.SaveChanges();
}

public void Put(string accNum, decimal amount)
{
    using (var db = new MyDbContext())
    {
        Put(accNum, amount, db);   
    }
}

The same with Withdraw method. Then you could write:

using (var db = new MyDbContext())
using (var tran = db.Database.BeginTransaction())
{
    try
    {
        Withdraw(srcAcc, amount, db);
        Put(destAcc, amount, db);

        tran.Commit();
    }
    catch
    {
        tran.Rollback();

        throw;
    }
}

Thanks to overloaded methods you don't have to refactor other existing methods to apply new parameter (MyDbContext).