2

I'm using EF Core in a netcoreapp3 app.

(I'm actually using DevArt's latest libs for Postgres but I don't think that's relevant here).

If I begin a transaction (repeatable read in this case):

var pg_conn = this.m_db.GetPGConnection();
pg_conn.Open();

// DevArt override BeginTransaction and allow you to pass in the isolation level
using (var trans = pg_conn.BeginTransaction(this.m_isolation))
{

   trans.StateChanged += Trans_StateChanged;

   try
   {
      T result = m_dlg(this.m_db);    // This delegate does stuff and calls db.SaveChanges
      trans.Commit();                 // By the time you get here the transaction has been auto committed and disposed
      return result;
   }

do some stuff and call SaveChanges the transaction has been committed and disposed.

I attached an event to the transaction's StateChanged event to see where the call was coming from. The stack trace shows that the call is coming from MS EF (not DevArt):

WFDB.dll!WFDB.Contexts.TransactionRepeater<WFDB.Contexts.Workflow_DBContext, object>.Trans_StateChanged(object sender, Devart.Common.TransactionStateChangedEventArgs e) Line 249   C#

Devart.Data.dll!Devart.Common.DbTransactionBase.OnStateChanged(Devart.Common.TransactionAction action, System.Data.Common.DbConnection connection)  Unknown

Devart.Data.PostgreSql.dll!Devart.Data.PostgreSql.PgSqlTransaction.Commit() Unknown

Devart.Data.PostgreSql.Entity.EFCore.dll!Devart.Common.Entity.cx.Commit()   Unknown

Microsoft.EntityFrameworkCore.Relational.dll!Microsoft.EntityFrameworkCore.Storage.RelationalTransaction.Commit()   Unknown (The culprit? How do I stop this call?)

Microsoft.EntityFrameworkCore.Relational.dll!Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(Microsoft.EntityFrameworkCore.DbContext _, (System.Collections.Generic.IEnumerable<Microsoft.EntityFrameworkCore.Update.ModificationCommandBatch>, Microsoft.EntityFrameworkCore.Storage.IRelationalConnection) parameters)    Unknown

Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.Storage.Internal.NoopExecutionStrategy.Execute<System.ValueTuple<System.__Canon, System.__Canon>, int>((System.__Canon, System.__Canon) state, System.Func<Microsoft.EntityFrameworkCore.DbContext, (System.__Canon, System.__Canon), int> operation, System.Func<Microsoft.EntityFrameworkCore.DbContext, (System.__Canon, System.__Canon), Microsoft.EntityFrameworkCore.Storage.ExecutionResult<int>> verifySucceeded)   Unknown

Microsoft.EntityFrameworkCore.Relational.dll!Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(System.Collections.Generic.IEnumerable<Microsoft.EntityFrameworkCore.Update.ModificationCommandBatch> commandBatches, Microsoft.EntityFrameworkCore.Storage.IRelationalConnection connection)  Unknown
Microsoft.EntityFrameworkCore.Relational.dll!Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(System.Collections.Generic.IList<Microsoft.EntityFrameworkCore.Update.IUpdateEntry> entries)  Unknown

Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(System.Collections.Generic.IList<Microsoft.EntityFrameworkCore.Update.IUpdateEntry> entriesToSave) Unknown

Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(bool acceptAllChangesOnSuccess)    Unknown

Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.DbContext.SaveChanges(bool acceptAllChangesOnSuccess)   Unknown

Microsoft.EntityFrameworkCore.dll!Microsoft.EntityFrameworkCore.DbContext.SaveChanges() Unknown

WFDB.dll!WFDB.Contexts.Workflow_DBContext.SaveChanges() Line 107    C#

I don't want the transaction to auto commit. How can it be prevented? There surely must be some setting in EF Core to stop that?

Adam Benson
  • 7,480
  • 4
  • 22
  • 45
  • You are bypassing EF Core infrastructure. Don't open db connection/transaction, use EF Core provided [BeginTransaction(IsolationLevel)](https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.relationaldatabasefacadeextensions.begintransaction?view=efcore-3.0) method – Ivan Stoev Dec 06 '19 at 01:04
  • But I don't have that call available to me, for some reason. When I enter this.m_db.Database.BeginTransaction(this.m_isolation); I get this error: error CS1501: No overload for method 'BeginTransaction' takes 1 arguments – Adam Benson Dec 06 '19 at 09:09
  • If I go to the definition of this.m_db.Database I get (from metadata) Assembly Microsoft.EntityFrameworkCore, Version=3.0.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60 ... which defines a class Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade which only has public virtual IDbContextTransaction BeginTransaction() and its Async brother. – Adam Benson Dec 06 '19 at 09:16
  • 1
    As you can see from the link, it's an extension method defined by Microsoft.EntityFrameworkCore.Relational *assembly*, so you have to reference it in order to make it available. – Ivan Stoev Dec 06 '19 at 09:16
  • 1
    Thanks, Ivan - I'll have a look around. – Adam Benson Dec 06 '19 at 09:18
  • 1
    Yep, that was it. I had to install that assembly from Nuget. I've used EF for quite a while but am new to EF Core and didn't even know that assembly existed. Thanks very much for your help. – Adam Benson Dec 06 '19 at 09:23
  • 1
    Also, you can wrap your DbContext in a global transaction (TransactionScope) instead of a local transaction. In this case, SaveChanges() should not create and commit the local transaction until committing the global one. – Devart Dec 09 '19 at 20:57

0 Answers0