I'm trying the EF6-way of using transactions and noticed that the following code
using (db.Database.BeginTransaction())
{
// something
using (db.Database.BeginTransaction())
{
/// something
}
}
throws an exception on the second begin transaction telling me that
The connection is already in a transaction and cannot participate in another
transaction. EntityClient does not support parallel transactions.
Not that I would want to do a parallel transaction, whatever that could mean on one connection. I just wanted a nested one.
So is this indeed not supported on what's going on?
You can use the CurrentTransaction
property.
using (db.Database.BeginTransaction())
{
if(db.Database.CurrentTransaction!=null)
{
/// something
}
else
{
using (db.Database.BeginTransaction())
{
/// the same something
}
}
}
The syntax is rather awkward. I often rely on an extension method to wrap the something into a transaction
public static void WrapInTransaction(this DbContext db, Action something)
{
if (db.Database.CurrentTransaction != null)
something();
else
using (db.Database.BeginTransaction())
{
something();
}
}
Limitation: I assume, when the inner transaction failed, the outer transaction should fail too. So you don't want to rollback and do something other on the inner one.
I just got the the problem, when overriding SaveChanges
, to apply some database change tracker, but don't know, if we already have a transaction. So this defines the usecase.
I created a custom transaction, which only starts a transaction, if we do not already have one. Any commit/rollback/dispose are only applied, when we had initial no transaction:
public class FallbackTransaction : IDisposable
{
private readonly System.Data.Entity.Database _database;
private DbContextTransaction _fallbackTransaction;
public FallbackTransaction(System.Data.Entity.Database database)
{
_database = database;
}
public void Dispose()
{
_fallbackTransaction?.Dispose();
}
public void Begin()
{
if (_database.CurrentTransaction == null)
{
_fallbackTransaction = _database.BeginTransaction();
}
}
public void Commit()
{
_fallbackTransaction?.Commit();
}
public void Rollback()
{
_fallbackTransaction?.Rollback();
}
}
Then I have created an extension method:
public static FallbackTransaction BeginFallbackTransaction(this System.Data.Entity.Database database)
{
var transaction = new FallbackTransaction(database);
transaction.Begin();
return transaction;
}
So we can do:
using (var transaction = dbContext.Database.BeginFallbackTransaction())
{
// do something
transaction.Commit();
}
This is based on ideas of the answer from jbl
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With