Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

entity framework transaction error

I am using entity framework and I have several methods where I am using transactions. I get this error: The connection is already in a transaction and cannot participate in another transaction. EntityClient does not support parallel transactions. I have multiple methods depending on 'MethodB' like the code example below:

public void MethodA(){
    using (var tran = Db.Database.BeginTransaction()){  
        MethodB();
        var tableARecord = new TableARecord();
        try
        {
            _context.TableAs.Add(tableARecord)
            Db.SaveChanges();
        }
        catch (Exception excp)
        {
            tran.Rollback();
            throw;
        }
    }
 }

 public void MethodC(){
    using (var tran = Db.Database.BeginTransaction()){  
        MethodB();
        //do something else
    }
 }

 public int MethodB(){
    int ret = 0
    //exception happens when starting the transaction below
    using (var tran = Db.Database.BeginTransaction()){  
        //do something else
    }
    return ret;
 }
like image 355
AAV Avatar asked Apr 25 '16 15:04

AAV


2 Answers

Before opening a new transaction, you need to commit the previous one, so you should not have opened a new transaction inside the previous one.

public void MethodA(){
    using (var tran = Db.Database.BeginTransaction()){  
        try
        {
            MethodB();
            var tableARecord = new TableARecord();
            _context.TableAs.Add(tableARecord)
            Db.SaveChanges();
        }
        catch (Exception excp)
        {
            tran.Rollback();
            throw;
        }
    }
 }

 public int MethodB(){
    int ret = 0
    //exception happens when starting the transaction below
    // The transaction is already open, you should not open a new one.

    //do something else
    return ret;
  }

but, the way you are doing it is an anti pattern. Every "save changes" that is not made when a transaction is opened, will be a single transaction.

The thing you should do is to begin your transaction inside your Business logic, and commit it at the same level.

// Business Logic :
var transaction = Db.Database.BeginTransaction())
try {
     _Repository.MethodA();
     _Repository.MethodB();
     transaction.Commit();
}
catch(){
     transaction.Rollback();
}


//Repository :

public void MethodA(){
    var tableARecord = new TableARecord();
    _context.TableAs.Add(tableARecord)
    Db.SaveChanges();
}

public void MethodA(){
    // Just do some other stuff
    Db.SaveChanges();
}
like image 98
Deblaton Jean-Philippe Avatar answered Oct 19 '22 19:10

Deblaton Jean-Philippe


Let me provide you an alternative for the already answered question.

You could check if a transaction is already created and use it instead.

public async Task MethodA()
{
    using(var transaction = await context.BeginTransaction() )
    {
        await MethodB(transaction);

        //...

        transaction.Commit();
    }
}

public async Task MethodB(IDbContextTransaction transaction)
{
    var isOpen = transaction != null;

    try
    {
        if (!isOpen)
        {
            transaction = await context.BeginTransaction();
        }

        //...

        if (!isOpen)
        {
            transaction.Commit();
        }
    }
    finally
    {
        if (!isOpen)
        {
            transaction.Dispose();
        }
    }
}

By calling MethodA(); that consequently calls MethodB(transaction);, it will use the current transaction

By calling MethodB(null);, it will create a new transaction and use it instead

like image 24
mtanksl Avatar answered Oct 19 '22 19:10

mtanksl