65.9K
CodeProject 正在变化。 阅读更多。
Home

在 EF Core 中更改主键数据类型

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2017年6月22日

CPOL

3分钟阅读

viewsIcon

27132

如何进行数据类型更改的迁移

引言

简单地更改主键的数据类型并不容易;您必须经过某些步骤。 一旦您遵循这些步骤,它就会变得容易。 本文展示了您需要做什么,因此您不必以艰难的方式弄清楚。 

背景

由于本文范围之外的原因,我们决定将某些表的主键从 int 更改为 guid。您可以使用任何列执行相同的操作,但对于主键,它有些不同。

使用代码

在 TFS 中,获取您的 EF core 项目的最新版本。

在包管理器控制台中,执行以下操作 以使代码和数据保持最新。

update-database -c XXEntities

在 TFS 中,检出文件 \path\YourDataProject\Migrations\XXEntitiesModelSnapshot.cs

在 包管理器控制台中,创建一个新的包:  

add-migration -c XXEntities NameOfMyUpdate

它将创建一个新的迁移,并且它是空的,并且设计器将完全反映数据库中字段的当前状态。 设计器是一个 .cs 文件,您将在项目管理器中的 NameOfMyUpdate 迁移文件下方看到它。 

您将看到一个“Up”方法和一个“Down”方法。 您将在其中放入代码来更改您的键的数据类型。

在您的设计器和 XXEntitiesModelSnapshot 中找到您想要的主键和外键。 将数据类型更改为您想要的类型

b.Property<Guid>("ColumnNameId");

当然,您还必须在整个应用程序中更改它。 每次您引用该列时,都要更改其数据类型。 

在到处更改它之后,重新检查设计器。 在所有文件中执行查找似乎找不到键,并且一切似乎都很顺利,但仍然是错误的,因为它们没有在此处更改。

在您新创建的迁移类中,在方法“Up”中,创建所有 EF Core 迁移调用以执行以下操作

               删除索引和约束

               将 Id 重命名为 Old Id

               创建正确数据类型的新 Id

               运行嵌入式 sql 语句来更新外键

               删除旧的 id

               创建新的索引和约束

我很幸运,在我的组织中,我不需要修改“Down”函数来反向执行所有这些功能。 如果您希望将您的数据更改回原始数据类型,您需要在 Down 函数中执行相反的操作。 

构建整个解决方案。在包管理器控制台中,再次执行以下操作 来调用您的新更改。

update-database -c XXEntities

以下显示了上述步骤的示例调用。 请注意,我还附带了 SQL 作为注释,以显示如何使用直接 SQL 完成它。 

 
//migrationBuilder.Sql("ALTER TABLE [dbo].[ContentTableName] DROP CONSTRAINT [FK_ContentTableName_MimeType_MimeTypeId]");
migrationBuilder.DropForeignKey(
    name: "FK_ContentTableName_MimeType_MimeTypeId",
    table: "ContentTableName");
 
//migrationBuilder.Sql("ALTER TABLE [dbo].[MimeType] DROP CONSTRAINT [PK_MimeType]");
migrationBuilder.DropPrimaryKey(
    "PK_MimeType"
    , "MimeType");
 
//migrationBuilder.Sql("DROP INDEX [IX_ContentTableName_MimeTypeId] ON [dbo].[ContentTableName]");
migrationBuilder.DropIndex(
    "IX_ContentTableName_MimeTypeId"
    , "ContentTableName");
 
//migrationBuilder.Sql("exec sp_rename 'dbo.MimeType.Id','OldId','COLUMN'");
migrationBuilder.RenameColumn(
    "Id"
    , "MimeType"
    , "OldId");
 
//migrationBuilder.Sql("alter table [dbo].[MimeType] add Id uniqueidentifier not null constraint df_MimeType_Id default(newid())");
migrationBuilder.AddColumn<Guid>(
    "Id"
    , "MimeType"
    , "uniqueidentifier"
    , defaultValue: "DF_MimeType_Id"
    , defaultValueSql: "newid()");
 
//migrationBuilder.Sql("exec sp_rename 'dbo.ContentTableName.MimeTypeId','OldMimeTypeId','COLUMN'");
migrationBuilder.RenameColumn(
    "MimeTypeId"
    , "ContentTableName"
    , "OldMimeTypeId");
 
//migrationBuilder.Sql("alter table dbo.ContentTableName add MimeTypeId uniqueidentifier null");
migrationBuilder.AddColumn<Guid>(
    name: "MimeTypeId",
    table: "ContentTableName",
    nullable: true
    );
 
migrationBuilder.Sql("update [dbo].ContentTableName set MimeTypeId=t.Id FROM[dbo].ContentTableName i inner join dbo.MimeType t on i.OldMimeTypeId = t.OldId");
 
////migrationBuilder.Sql("ALTER TABLE [dbo].[MimeType] ADD  CONSTRAINT [PK_MimeType] PRIMARY KEY CLUSTERED ([Id] ASC) WITH(PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)");
migrationBuilder.AddPrimaryKey(
    "PK_MimeType"
    , "MimeType",
    "Id");
 
////migrationBuilder.Sql("ALTER TABLE [dbo].[ContentTableName]  WITH CHECK ADD  CONSTRAINT [FK_ContentTableName_MimeType_MimeTypeId] FOREIGN KEY([MimeTypeId]) REFERENCES[dbo].[MimeType]([Id])");
////migrationBuilder.Sql("ALTER TABLE [dbo].[ContentTableName] CHECK CONSTRAINT [FK_ContentTableName_MimeType_MimeTypeId]");
migrationBuilder.AddForeignKey(
            name: "FK_ContentTableName_MimeType_MimeTypeId",
            table: "ContentTableName",
            column: "MimeTypeId",
            principalTable: "MimeType",
            principalColumn: "Id",
            onDelete: ReferentialAction.Restrict);
 
////migrationBuilder.Sql("CREATE NONCLUSTERED INDEX [IX_ContentTableName_MimeTypeId] ON [dbo].[ContentTableName] ([MimeTypeId] ASC ) WITH(PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)");
migrationBuilder.CreateIndex(
    name: "IX_ContentTableName_MimeTypeId",
    table: "ContentTableName",
    column: "MimeTypeId");
 
////migrationBuilder.Sql("ALTER TABLE [dbo].[MimeType] DROP COLUMN [OldId]");
migrationBuilder.DropColumn(
    "OldId"
    , "MimeType");
 
////migrationBuilder.Sql("ALTER TABLE [dbo].ContentTableName DROP COLUMN OldMimeTypeId");
migrationBuilder.DropColumn(
    "OldMimeTypeId"
    , "ContentTableName");
 

 

关注点

想要确保您找到了并更改了所有内容? 再次执行 add-update -C XXEntities NameOfUpdate2。如果您正确地在每个地方进行了更改,Up 函数和 Down 函数将是空的。 如果您忘记在某个地方更改您的数据类型,这两个函数将有很多生成的代码来尝试将您的数据库修复到某种状态。   从长远来看,这种额外的检查是有回报的;如果在意识到您的错误并在以后修复它之前,您执行了更多的 EF Core 迁移,事情会变得更加复杂。   

坏消息[或 EF Core 数据类型更改问题]不像美酒;它们不会随着年龄的增长而变得更好。 

历史

20170621  初始版本

© . All rights reserved.