question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Changes not committed by SaveChanges for single-statement updates (probably EF7-related)

See original GitHub issue

Summary

After upgrading a project to EF7 and Pomelo.EntityFrameworkCore.MySql 7.0.0-silver.1, certain changes to the database stopped working, without any errors. Everything seemed to work, but the changes were never committed to the database. As far as we can see, this affected only changes that generated single statements (as in deleting a record from a table).

The details haven’t been fully investigated, but we finally found a workaround, and I’m creating this issue in case others encounter the same problem - there’s also a chance that the cause is obvious to the maintainers when they see this. I’ll try to do some further testing and research over the weekend.

Steps to reproduce

In a .NET 7.0 (Windows x64) project, we use these nuget packages (and whatever else is added automagically):

  • Microsoft.EntityFrameworkCore 7.0.1
  • Pomelo.EntityFrameworkCore.MySql 7.0.0-silver.1
  • MySqlConnector 2.2.5

We connect to a database that runs MySQL version 5.6.21-log, using InnoDB. Yes, it’s old but that’s out of our control at the moment.

Configuration of our DbContext:

            services.AddDbContext<TestDbContext>(option =>
            {
                option.UseMySql(testDbConnectionString, new MySqlServerVersion("5.6.21-log"))
                    .EnableSensitiveDataLogging()
                    .EnableDetailedErrors();
            });

The issue

Single-statement database changes (SaveChanges) seem to work according to all logging, but the changes never get committed.

Running this on a freshly injected context results in one affected record, but it’s still in the database (note that CurrentTransaction on context.Database is null, as expected, before the second line is executed):

context.Remove<SomeEntity>(entity);
var count = context.SaveChanges(); // count is 1 after execution

No exceptions, no error messages. The log looks like this:

Microsoft.EntityFrameworkCore.Database.Command: Information: Executed DbCommand (17ms) [Parameters=[@p0='1234'], CommandType='Text', CommandTimeout='30']
DELETE FROM `SomeTable`
WHERE `Id` = @p0;
SELECT ROW_COUNT();

Workaround

Add this in the constructor for the context:

Database.AutoTransactionBehavior = AutoTransactionBehavior.Always;

This makes the problem go away.

Suspicion

When AutoTransactionBehaviour is the default (AutoTransactionBehaviour.WhenNeeded), autocommit (MySql “SET autocommit=0”) is set to 0 somewhere, which is wrong (it doesn’t turn off implicit transactions, it just turns off committing them automatically). InnoDB starts implicit transactions ALWAYS, and it can’t be turned off - a commit is ALWAYS required, disabling autocommit just stops it from happening automatically.

Further details

See my comments on #1620 - as you can see, I got some help from @roji on this.

This is the change in EF7 that this bug is most likely caused by: https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#unneeded-transactions-are-eliminated

Issue Analytics

  • State:open
  • Created 9 months ago
  • Comments:13

github_iconTop GitHub Comments

2reactions
lauxjpncommented, Jan 7, 2023

The Pomelo provider currently does not issue any autocommit statements (we might change that in the future, in a similar way as SQL Server does). Since according to the MySQL docs, autocommit always starts as enabled when a session is created, it is likely that the app’s code, the connection string or the database configuration disables it:

15.7.2.2 autocommit, Commit, and Rollback

In InnoDB, all user activity occurs inside a transaction. If autocommit mode is enabled, each SQL statement forms a single transaction on its own. By default, MySQL starts the session for each new connection with autocommit enabled, so MySQL does a commit after each SQL statement if that statement did not return an error. […]


@nightcoder62 To get to the bottom of this issue, let’s start by checking first, that autocommit is actually turned off right before the entity is supposed to be removed (so right before the SaveChanges() call), by executing the following statement from the MySQL connection your DbContext is using from within your app:

select @@global.autocommit, @@session.autocommit;

For example:

context.Database.OpenConnection();
var connection = context.Database.GetDbConnection();

using var command = connection.CreateCommand();
command.CommandText = "select @@global.autocommit, @@session.autocommit;";

using var dataReader = command.ExecuteReader();
if (dataReader.Read())
{
    var globalAutoCommit = dataReader[0];
    var sessionAutoCommit = dataReader[1];

    Logger.LogInformation($"@@global.autocommit = {globalAutoCommit}, @@session.autocommit = {sessionAutoCommit}");
}

Then check the log, whether it is the global or session autocommit, that is 0. Once we know that, we can continue investigating the issue further.

1reaction
Nefcantocommented, Jan 26, 2023

Well, after we prepared a MRE, we realized that it works just fine. As we investigated, it turned out to be a bug in our view that one colleague caused. The view did not return the record because of a wronly configured join.

However, when we put “SaveChanges” and “Find” after each other and from two different instantiated contexts, it returns null. I’m still investigating this.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Entity Framework SaveChanges() not updating the database
I discovered that if you call SaveChanges() on an object that hasn't been modified, ... To force an update without changing any fields:...
Read more >
EF: When SaveChanges does not save changes
I was debugging an issue with DbContext.SaveChanges which was neither saving changes to the database nor throwing any exceptions, ...
Read more >
Basic SaveChanges - EF Core
SaveChanges () is one of two techniques for saving changes to the database with EF. With this method, you perform one or more...
Read more >
ObjectContext.SaveChanges Method (System.Data.Objects)
SaveChanges ()​​ Persists all updates to the data source and resets change tracking in the object context.
Read more >
Entity Framework Core verify SaveChanges count
I am using ASP.NET Core 3.1 and have been assigned a task to verify the count of changes done using SaveChanges() . It...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found