Changes not committed by SaveChanges for single-statement updates (probably EF7-related)
See original GitHub issueSummary
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:
- Created 9 months ago
- Comments:13
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:@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 theSaveChanges()
call), by executing the following statement from the MySQL connection yourDbContext
is using from within your app:For example:
Then check the log, whether it is the global or session
autocommit
, that is0
. Once we know that, we can continue investigating the issue further.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.