You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
EF Core 9.0.0 seems to have introduced a bug which was not present in previous versions.
Our existing migration file has the following content:
usingMicrosoft.EntityFrameworkCore.Migrations;namespaceAuthorization.Infrastructure.Migrations{publicpartialclassAdaptEntitiesForNewEndpoints:Migration{protectedoverridevoidUp(MigrationBuildermigrationBuilder){migrationBuilder.DropForeignKey(name:"FK_ModulePermission_Module_ModuleId",schema:"authorization",table:"ModulePermission");migrationBuilder.RenameTable(name:"Module",newName:"ModuleGroup",newSchema:"authorization",schema:"authorization");migrationBuilder.CreateTable(name:"Module",schema:"authorization",columns: table =>new{Id=table.Column<int>(type:"int",nullable:false).Annotation("SqlServer:Identity","1, 1"),ModuleId=table.Column<string>(type:"nvarchar(max)",nullable:true)},constraints: table =>{table.PrimaryKey("PK_Module", x =>x.Id).Annotation("SqlServer:Clustered",true);});
As you can see our migration file (created by EFCore, code first) contains a
RenameTable from Module to ModuleGroup
The next command is a
CreateTable Module
So the old existing Module table should be renamed to ModuleGroup and a new table also named Module should be created.
This constellation produces a
KeyNotFoundException
The error is in class SqlServerMigrationsSqlGenerator.
Below code is directly from the SqlServerMigrationsSqlGenerator class. You see that the first RenameTable operation is addressed in the switch/case block
caseRenameTableOperationrenameTableOperation:{if(temporalInformationisnull){temporalInformation=BuildTemporalInformationFromMigrationOperation(schema,renameTableOperation);}varisTemporalTable=renameTableOperation[SqlServerAnnotationNames.IsTemporal]asbool?==true;if(isTemporalTable){DisableVersioning(tableName,schema,temporalInformation,suppressTransaction,shouldEnableVersioning:true);}operations.Add(operation);// since table was renamed, update entry in the temporal info maptemporalTableInformationMap[(renameTableOperation.NewName!,renameTableOperation.NewSchema)]=temporalInformation;temporalTableInformationMap.Remove((tableName,schema));break;}
In this case block the table name is removed from dictionary temporalTableInformationMap (You can see the full code posted below).
The next foreach loop (foreach (var operation in migrationOperations)) addresses the CreateTable operation with the same name as the renamed one.
This code then produces the KeyNotFoundException because the temporalTableInformationMap does no longer contain an entry for table name "Module" in the dict "temporalTableInformationMap" because it has been removed from the dict in the prior loop (renameTable operation).
// we are guaranteed to find entry here - we looped through all the operations earlier,// info missing from operations we got from the model// and in case of no/incomplete model we created dummy (non-temporal) entriesvartemporalInformation=temporalTableInformationMap[(tableName,rawSchema)];
So it is definitely not guaranteed that the entry is present in the temporalTableInformationMap as stated in the comment above.
Below you can find the complete snippet from class SqlServerMigrationsSqlGenerator.
// now we do proper processing - for table operations we look at the annotations on them// and continuously update the stored temporal info as the table is being modified// for column (and other) operations we don't have annotations on them, so we look into the// information we stored in the initial pass and updated in when processing table ops that happened earlierforeach(varoperationinmigrationOperations){if(operationisEnsureSchemaOperationensureSchemaOperation){availableSchemas.Add(ensureSchemaOperation.Name);}if(operationis not ITableMigrationOperationtableMigrationOperation){operations.Add(operation);continue;}vartableName=tableMigrationOperation.Table;varrawSchema=tableMigrationOperation.Schema;varsuppressTransaction=IsMemoryOptimized(operation,model,rawSchema,tableName);varschema=rawSchema??model?.GetDefaultSchema();// we are guaranteed to find entry here - we looped through all the operations earlier,// info missing from operations we got from the model// and in case of no/incomplete model we created dummy (non-temporal) entriesvartemporalInformation=temporalTableInformationMap[(tableName,rawSchema)];switch(operation){caseCreateTableOperationcreateTableOperation:{// for create table we always generate new temporal information from the operation itself// just in case there was a table with that name before that got deleted/renamed// this shouldn't happen as we re-use existing tables rather than drop/recreate// but we are being extra defensive here// and also, temporal state (disabled versioning etc.) should always reset when creating a tabletemporalInformation=BuildTemporalInformationFromMigrationOperation(schema,createTableOperation);if(temporalInformation.IsTemporalTable&&temporalInformation.HistoryTableSchema!=schema&&temporalInformation.HistoryTableSchema!=null&&!availableSchemas.Contains(temporalInformation.HistoryTableSchema)){operations.Add(newEnsureSchemaOperation{Name=temporalInformation.HistoryTableSchema});availableSchemas.Add(temporalInformation.HistoryTableSchema);}operations.Add(operation);break;}caseDropTableOperationdropTableOperation:{varisTemporalTable=dropTableOperation[SqlServerAnnotationNames.IsTemporal]asbool?==true;if(isTemporalTable){// if we don't have temporal information, but we know table is temporal// (based on the annotation found on the operation itself)// we assume that versioning must be disabled, if we have temporal info we can check properlyif(temporalInformationisnull||!temporalInformation.DisabledVersioning){AddDisableVersioningOperation(tableName,schema,suppressTransaction);}if(temporalInformationis not null){temporalInformation.ShouldEnableVersioning=false;temporalInformation.ShouldEnablePeriod=false;}operations.Add(operation);varhistoryTableName=dropTableOperation[SqlServerAnnotationNames.TemporalHistoryTableName]asstring;varhistoryTableSchema=dropTableOperation[SqlServerAnnotationNames.TemporalHistoryTableSchema]asstring??schema;vardropHistoryTableOperation=newDropTableOperation{Name=historyTableName!,Schema=historyTableSchema};operations.Add(dropHistoryTableOperation);}else{operations.Add(operation);}// we removed the table, so we no longer need it's temporal information// there will be no more operations involving this tabletemporalTableInformationMap.Remove((tableName,schema));break;}caseRenameTableOperationrenameTableOperation:{if(temporalInformationisnull){temporalInformation=BuildTemporalInformationFromMigrationOperation(schema,renameTableOperation);}varisTemporalTable=renameTableOperation[SqlServerAnnotationNames.IsTemporal]asbool?==true;if(isTemporalTable){DisableVersioning(tableName,schema,temporalInformation,suppressTransaction,shouldEnableVersioning:true);}operations.Add(operation);// since table was renamed, update entry in the temporal info maptemporalTableInformationMap[(renameTableOperation.NewName!,renameTableOperation.NewSchema)]=temporalInformation;temporalTableInformationMap.Remove((tableName,schema));break;}
Please let me know if you need additional information.
EF Core version: 9.0.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET8.0
Operating system: Windows
IDE: Visual Studio 2022 17.12.3
The text was updated successfully, but these errors were encountered:
EF Core 9.0.0 seems to have introduced a bug which was not present in previous versions.
Our existing migration file has the following content:
As you can see our migration file (created by EFCore, code first) contains a
The next command is a
So the old existing Module table should be renamed to ModuleGroup and a new table also named Module should be created.
This constellation produces a
The error is in class SqlServerMigrationsSqlGenerator.
Below code is directly from the SqlServerMigrationsSqlGenerator class. You see that the first RenameTable operation is addressed in the switch/case block
In this case block the table name is removed from dictionary temporalTableInformationMap (You can see the full code posted below).
The next foreach loop (foreach (var operation in migrationOperations)) addresses the CreateTable operation with the same name as the renamed one.
This code then produces the KeyNotFoundException because the temporalTableInformationMap does no longer contain an entry for table name "Module" in the dict "temporalTableInformationMap" because it has been removed from the dict in the prior loop (renameTable operation).
So it is definitely not guaranteed that the entry is present in the temporalTableInformationMap as stated in the comment above.
Below you can find the complete snippet from class SqlServerMigrationsSqlGenerator.
Please let me know if you need additional information.
EF Core version: 9.0.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET8.0
Operating system: Windows
IDE: Visual Studio 2022 17.12.3
The text was updated successfully, but these errors were encountered: