Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SqlServer Migrations: Rebuild primary keys when column type changed #16758

Open
Tracked by #22946
chaim1221 opened this issue Jul 26, 2019 · 4 comments
Open
Tracked by #22946

SqlServer Migrations: Rebuild primary keys when column type changed #16758

chaim1221 opened this issue Jul 26, 2019 · 4 comments

Comments

@chaim1221
Copy link

Describe what is not working as expected.

Okay, so the scenario is, I have a database-first EF Core migration called "Prehistoric" and then a migration on top of it with a newer schema called "Initial Migration." This is because I generated the "Prehistoric" migration from the schema of a .NET 4.5.1 project using EF 6. Then, I created a new migration to handle the changes to EF Core and get rid of some unused tables.

The new migration was done with ef migrations add. The old migration is just a database first migration that has the entire legacy schema. The migrations are in different tables for the different phases of the project, and the older migrations history is deleted by EF Core in the new migration. No worries, I don't really need it (yet).

In any case, I can't undo the migration created with EF Core, because I get this error. I can't drop the PK constraint on the table (as far as I know).

If you are seeing an exception, include the full exceptions details (message and stack trace).

fail: Microsoft.EntityFrameworkCore.Database.Command[20102]
      Failed executing DbCommand (49ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      DECLARE @var18 sysname;
      SELECT @var18 = [d].[name]
      FROM [sys].[default_constraints] [d]
      INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
      WHERE ([d].[parent_object_id] = OBJECT_ID(N'[AspNetUsers]') AND [c].[name] = N'Id');
      IF @var18 IS NOT NULL EXEC(N'ALTER TABLE [AspNetUsers] DROP CONSTRAINT [' + @var18 + '];');
      ALTER TABLE [AspNetUsers] ALTER COLUMN [Id] nvarchar(128) NOT NULL;
System.Data.SqlClient.SqlException (0x80131904): The object 'PK_dbo.AspNetUsers' is dependent on column 'Id'.
ALTER TABLE ALTER COLUMN Id failed because one or more objects access this column.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async, Int32 timeout, Boolean asyncWrite)
   at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite, String methodName)
   at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConnection connection, DbCommandMethod executeMethod, IReadOnlyDictionary`2 parameterValues)
ClientConnectionId:5da82a16-4a29-43a3-b7da-372e63aa2afe
Error Number:5074,State:1,Class:16
Failed executing DbCommand (49ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
DECLARE @var18 sysname;
SELECT @var18 = [d].[name]
FROM [sys].[default_constraints] [d]
INNER JOIN [sys].[columns] [c] ON [d].[parent_column_id] = [c].[column_id] AND [d].[parent_object_id] = [c].[object_id]
WHERE ([d].[parent_object_id] = OBJECT_ID(N'[AspNetUsers]') AND [c].[name] = N'Id');
IF @var18 IS NOT NULL EXEC(N'ALTER TABLE [AspNetUsers] DROP CONSTRAINT [' + @var18 + '];');
ALTER TABLE [AspNetUsers] ALTER COLUMN [Id] nvarchar(128) NOT NULL;
System.Data.SqlClient.SqlException (0x80131904): The object 'PK_dbo.AspNetUsers' is dependent on column 'Id'.

etc.

Steps to reproduce

Include a complete code listing (or project/solution) that we can run to reproduce the issue.

Honestly, I have no idea. The project as it stands is located at https://github.com/chaim1221/Machete (master is having the problem). It's not really a problem for everyone who wants to stay on the new system, but we may need to roll one client back because our new code is having trouble handling the volume, and we can't, because of this issue.

Partial code listings, or multiple fragments of code, will slow down our response or cause us to push the issue back to you to provide code to reproduce the issue.

I understand but I really have no idea how to manually reproduce this.

Further technical details

EF Core version: 2.2.4
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Mac OS X for dev, Debian 9 in prod. Prod does not matter to this problem.
IDE: Rider 2019.1.1 (but I am not using the IDE, I am using the CLI).

@chaim1221
Copy link
Author

It's worth noting that the above-linked project is an MVVM type project with the service logic and the entity definitions in separate layers. So the EF stuff is in "Data" (but naturally we run the migrations through the MVC project, Web).

@ajcvickers ajcvickers added this to the Backlog milestone Jul 29, 2019
@bricelam bricelam self-assigned this Nov 4, 2019
@ajcvickers ajcvickers modified the milestones: Backlog, 5.0.0 Nov 13, 2019
@ajcvickers ajcvickers modified the milestone: 5.0.0 Nov 15, 2019
@bricelam bricelam changed the title AspNetUsers is dependent on column 'Id' ... ALTER TABLE ALTER COLUMN Id failed SqlServer Migrations: Rebuild primary keys when column narrowed May 29, 2020
@ajcvickers ajcvickers modified the milestones: 5.0.0, Backlog Jun 9, 2020
@bricelam bricelam modified the milestones: Backlog, 6.0.0 Nov 13, 2020
@marchy
Copy link

marchy commented Oct 23, 2023

What's a viable workaround for when running into this issue?

Any way to add manual SQL commands or create a multi-step migration to get around the issue?
(ie: drop foreign relations altogether from all referencing tables, then upgrade the original table, then re-add references back in)

@ajcvickers
Copy link
Contributor

@marchy
Copy link

marchy commented Oct 24, 2023

Thanks @ajcvickers, I meant what are the actual SQL commands to issue.

However we ended up figuring out a much nicer way to do it with a two-part migration:

Part I: Create a migration (ie: MyMigration_Part1) where you temporarily sever the referencing foreign keys

class MyDBContext : DbContext {
	...

	class RegionConfig : IEntityTypeConfiguration<Region> {
		public void Configure( EntityTypeBuilder<Region> region ){
			region.HasKey( _ => _.Identifier )
				.IsClustered(); // NOTE: requires column rebuilding
			region.Property( _ => _.Identifier )
				.HasMaxLength( ColumnLength_RegionIdentifier ); // NOTE: requires foreign keys rebuilding
				
			// .. other field mappings
		}
	}

	// NOTE: Do this for all entities that reference the entity with the table columns that need to be rebuilt:
	class SomeReferencingEntityConfig : IEntityTypeConfiguration<SomeReferencingEntityConfig> {
		public void Configure( EntityTypeBuilder<SomeReferencingEntityConfig> referencingEntity ){
			// .. other field mappings
			
			/*TEMP*/referencingEntity.Ignore( _ => _.Region );
//			referencingEntity.HasOne( _ => _.Region )
//				.WithMany()
//				.HasForeignKey( _ => _.RegionIdentifier );

			// .. other field mappings
		}
	}
	
	...

This will remove all the existing foreign keys and indexes to the original table, allowing the table-rebuilding change to go through EF alone.

Part II: Create a migration (ie: MyMigration_Part2) where you revive the reference fields.

class MyDBContext : DbContext {
	...

	class SomeReferencingEntityConfig : IEntityTypeConfiguration<SomeReferencingEntityConfig> {
		public void Configure( EntityTypeBuilder<SomeReferencingEntityConfig> referencingEntity ){
			// .. other field mappings
			
			referencingEntity.HasOne( _ => _.Region )
				.WithMany()
				.HasForeignKey( _ => _.RegionIdentifier );

			// .. other field mappings
		}
	}
	
	...

Worked blissfully!

No data was dropped from any of the tables since it's only foreign keys that indexes that got dropped in the first-part migration – then re-added in the second part. And EF did all the model delta detections correctly with no hand-written SQL needed. 🚀

@ajcvickers ajcvickers changed the title SqlServer Migrations: Rebuild primary keys when column narrowed SqlServer Migrations: Rebuild primary keys when column type changed Jan 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants