Repository navigation
Rowversion migration regression from EF Core 8 to 9 #35148
Description
Rowversion migration regression from EF Core 8 to 9
I'm in the process of upgrading an application from .NET 8 to 9.
But I'm running into problems when trying to run existing migrations that have been created with EF Core 8 and working for over a year now on multiple versions of EF Core (8.0.4 to 8.0.11)
I have a migration that throws exceptions and can't complete at all on v9.0.0, but if I revert to 8.0.11 it works fine.
Include your code
The migration that fails is a rename of a table where we use rowversion configured like this:
public void Configure(EntityTypeBuilder<Sensor> builder)
{
builder.ToTable(nameof(MasterCatalogueDbContext.Sensors), schema: DatabaseConstants.MasterDataCatalogueSchemaName);
// Optimistic Concurrency token
builder.Property(p => p.RowVersion).IsRowVersion();
}With this migration created for altering the rowversion column:
migrationBuilder.AlterColumn<byte[]>(
name: "RowVersion",
schema: "attributeList",
table: "MeasurementUnits",
type: "rowversion",
rowVersion: true,
nullable: false,
defaultValue: Array.Empty<byte>(),
oldClrType: typeof(byte[]),
oldType: "rowversion",
oldRowVersion: true,
oldDefaultValue: Array.Empty<byte>())
.Annotation("SqlServer:IsTemporal", true)
.Annotation("SqlServer:TemporalHistoryTableName", "MeasurementUnitsHistory")
.Annotation("SqlServer:TemporalHistoryTableSchema", "attributeList")
.Annotation("SqlServer:TemporalPeriodEndColumnName", "PeriodEnd")
.Annotation("SqlServer:TemporalPeriodStartColumnName", "PeriodStart")
.OldAnnotation("SqlServer:IsTemporal", true)
.OldAnnotation("SqlServer:TemporalHistoryTableName", "SensorUnitsHistory")
.OldAnnotation("SqlServer:TemporalHistoryTableSchema", "attributeList")
.OldAnnotation("SqlServer:TemporalPeriodEndColumnName", "PeriodEnd")
.OldAnnotation("SqlServer:TemporalPeriodStartColumnName", "PeriodStart");And that throws this error:
2024-11-14T15:17:05 [15:17:05 INF] Applying migration '20240610063634_RenameSensorUnitToMeasurementUnit'.
2024-11-14T15:17:05 [15:17:05 ERR] Failed executing DbCommand (32ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
2024-11-14T15:17:05 DECLARE @var15 sysname;
2024-11-14T15:17:05 SELECT @var15 = [d].[name]
2024-11-14T15:17:05 FROM [sys].[default_constraints] [d]
2024-11-14T15:17:05 INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
2024-11-14T15:17:05 WHERE ([d].[parent_object_id] = OBJECT_ID(N'[attributeList].[MeasurementUnits]') AND [c].[name] = N'RowVersion');
2024-11-14T15:17:05 IF @var15 IS NOT NULL EXEC(N'ALTER TABLE [attributeList].[MeasurementUnits] DROP CONSTRAINT [' + @var15 + '];');
2024-11-14T15:17:05 ALTER TABLE [attributeList].[MeasurementUnits] ALTER COLUMN [RowVersion] rowversion NOT NULL;
2024-11-14T15:17:05 ALTER TABLE [attributeList].[MeasurementUnits] ADD DEFAULT 0x FOR [RowVersion];
2024-11-14T15:17:05 [15:17:05 ERR] Failed to migrate MasterCatalogue database!
2024-11-14T15:17:05 Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot alter column 'RowVersion' to be data type timestamp.
2024-11-14T15:17:05 at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
2024-11-14T15:17:05 at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
2024-11-14T15:17:05 at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
2024-11-14T15:17:05 at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
2024-11-14T15:17:05 at Microsoft.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean isAsync, Int32 timeout, Boolean asyncWrite)
2024-11-14T15:17:05 at Microsoft.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry, String methodName)
2024-11-14T15:17:05 at Microsoft.Data.SqlClient.SqlCommand.ExecuteNonQuery()
2024-11-14T15:17:05 at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteNonQuery(RelationalCommandParameterObject parameterObject)
2024-11-14T15:17:05 at Microsoft.EntityFrameworkCore.Migrations.MigrationCommand.ExecuteNonQuery(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues)
2024-11-14T15:17:05 at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationCommandExecutor.Execute(IReadOnlyList`1 migrationCommands, IRelationalConnection connection, MigrationExecutionState executionState, Boolean beginTransaction, Boolean commitTransaction, Nullable`1 isolationLevel)
2024-11-14T15:17:05 at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationCommandExecutor.<>c.<ExecuteNonQuery>b__3_1(DbContext _, ValueTuple`6 s)
2024-11-14T15:17:05 at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
2024-11-14T15:17:05 at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationCommandExecutor.ExecuteNonQuery(IReadOnlyList`1 migrationCommands, IRelationalConnection connection, MigrationExecutionState executionState, Boolean commitTransaction, Nullable`1 isolationLevel)
2024-11-14T15:17:05 at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.MigrateImplementation(DbContext context, String targetMigration, MigrationExecutionState state, Boolean useTransaction)
2024-11-14T15:17:05 at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.<>c.<Migrate>b__20_1(DbContext c, ValueTuple`4 s)
2024-11-14T15:17:05 at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
2024-11-14T15:17:05 at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
2024-11-14T15:17:05 at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.Migrate(DatabaseFacade databaseFacade)
2024-11-14T15:17:05 at RTDT.MasterCatalogue.Web.Program.MigrateAndSeed(WebApplication app) in C:\GitVestas\RTDT\src\Services\RTDT.MasterCatalogue\src\RTDT.MasterCatalogue.Web\Program.cs:line 171
2024-11-14T15:17:05 ClientConnectionId:f4a440da-ec8a-43b2-9051-87cbd87b6faa
2024-11-14T15:17:05 Error Number:4927,State:1,Class:16
Include provider and version information
EF Core version: 9.0.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 9.0
Operating system: Windows & Linux (Local & CICD)
IDE: Visual Studio Enterprise 2022 17.12.0
Activity
Moerup commentedon Nov 19, 2024
Extracted the rowversion issue from this other temporal issue: #35108 based on @roji comment: #35108_
ajcvickers commentedon Dec 4, 2024
@Moerup I have not been able to reproduce this. Please attach a small, runnable project or post a small, runnable code listing that contains a full model in code that produces the initial migration and then specifically how to change that model to reproduce the migration that you are seeing.
Moerup commentedon Dec 4, 2024
@ajcvickers Have you tried testing it with a table using temporal tables?
I have another issue open, I also mentioned in the other comment (#35108), and maybe they are clashing and it's not related directly to the rowversion.
I sadly don't currently have the resources in the project to spend time on a standalone reproduction of the issues, would love to do it, but the money guys says no, as is tradition 😄
danroot commentedon Dec 4, 2024
@ajcvickers I'm not 100% sure, but my repro from the other ticket may help here also. The issue there is a migration created by EFCore 8 that is then applied by EFCore 9. In that case, we have unit tests that recreate the database using migrations. These tests fail when switching to EFCore 9, running EFCore 8-generated migration for a temporal table. I suspect similar in this case, and can try to fork the repository to prove it out. In general:
danroot commentedon Dec 4, 2024
Full repro of this error. It's worth noting I could not get it to repro if I just had RowVersion migration. The issue is a combination of RowVersion+TemporalTable migrations generated from EFCore8, when run by EFCore9 causes the error "Cannot alter column 'RowVersion' to be data type timestamp". This is a different error from the other ticket, but I suspect being caused by the same area of EFCore9 code. I suspect these changes, but haven't dove into the EFCore code to try to figure out further.
In our case, the related issue is blocking EFCore9 adoption. Possible workarounds include modifying the migration cs somehow or complete reset of migrations.
Moerup commentedon Dec 4, 2024
Thanks a lot @danroot for taking the time to make repros of not only 1, but 2 of my created issues!
Fix to #35108 - Temporal table migration regression from EF Core 8 to 9
Fix to #35108 - Temporal table migration regression from EF Core 8 to 9
Fix to #35108 - Temporal table migration regression from EF Core 8 to 9
killnine commentedon Jan 6, 2025
Thanks for the legwok @danroot, this regression hit my project too as I was updating to .NET 9. I have the exact situation where my solution originally did not have temporal tables applied to entities but they were added later. Now my CI pipeline fails with the error about
Pretty tricky and will prevent this update from going to production. So does a fix look to be heading for 9.0.2?
danroot commentedon Jan 7, 2025
Yes, my understanding is this is fixed for 9.0.2 in #35289
If you need fix before that, @maumar 's fix here works.. I even looked into writing a script to fixup ours in the way he suggests (we have dozens of migrations with this issue), but couldn't quite get a script I was comfortable with.
An alternative, IF you don't have to support any old-version databases, would be to squash migrations. and start over. That makes most sense in like an enterprise environment where you know all dev, test, and production instances of the db are up-to-date.