EFCore之项目升级遇到迁移小问题

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EFCore之项目升级遇到迁移小问题相关的知识,希望对你有一定的参考价值。

前言

今天遇到了一个很奇怪的问题,这个项目原来的版本是.NetCore3.1版本,然后我就趁着这次的需求的开发就想给升级到.Net6版本,我也是第一次接触这个项目。

其实.Net6也不算新了,组内很多项目都已经升级了

遇到问题

开始测试后,测试同学反馈在新库测试的时候就发现程序起不来,有错误信息,并且错误信息看得我一脸懵逼。

首先这个项目启动后会使用EFCore进行数据库迁移,并且初始化后会插入一些基础数据,然后在插入数据的时候,这个表的某个列应该是text类型的,可是迁移后数据库中该列是int类型。这个时候我肯定要先甩锅了,虽然我在这期使用命令行添加过字段生成过迁移文件,但是我这期需求都没涉及这个表以及我才接触这个项目,肯定不是我的原因,嘿嘿

当然问题还是要排查的,排查发现

1、这个表的这个列是在之前又一次迁移的时候列从int改为了text

2、最新版本的迁移文件快照中,这个列是字符串类型,是没问题的

3、找到上个版本的代码,然后去生成数据库也是没问题的(看到这里我就知道亏了,这个锅还是在我头上了)

4、检查我当前需求的的迁移文件(当时开发时候也检查过的),虽然我只是加了几个字段,但是这次迁移文件有1000多行,主要是自动生成的对表的一些注释等操作代码(升级后的迁移文件内容更规范了),没有涉及到那个列的操作

5、我将本期需求生成的迁移文件给删除掉,然后从上个版本中拷贝迁移快照,生成数据库,发现列还是int

6、懵逼。。。

7、那么我就看看迁移的生成的SQL吧,然后就发现了原来如此

情景重现

我创建一个.NetCore3.1版本的Api项目,安装3.x版本的nuget包

<ItemGroup>
  <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.32">
    <PrivateAssets>all</PrivateAssets>
    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
  </PackageReference>
  <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.18" />
</ItemGroup>

创建实体类

public class UserInfo

    public int Id  get; set; 

    public string Name  get; set; 

    public int Type  get; set; 

以及上下文

public class OpenDbContext : DbContext

    public OpenDbContext(DbContextOptions options) : base(options)
    
    

    public DbSet<UserInfo> UserInfos  get; set; 

注入配置

services.AddDbContext<OpenDbContext>(options =>

    options.UseNpgsql(connection);
);

然后我使用EFCore CLI在项目目录下去生成迁移文件(不了解的话可以看这里:https://learn.microsoft.com/zh-cn/ef/core/cli/dotnet)

dotnet ef migrations add Init

然后项目生成了迁移文件如下

public partial class Init : Migration

    protected override void Up(MigrationBuilder migrationBuilder)
    
        migrationBuilder.CreateTable(
            name: "UserInfos",
            columns: table => new
            
                Id = table.Column<int>(nullable: false)
                    .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
                Name = table.Column<string>(nullable: true),
                Type = table.Column<int>(nullable: false)
            ,
            constraints: table =>
            
                table.PrimaryKey("PK_UserInfos", x => x.Id);
            );
    

    protected override void Down(MigrationBuilder migrationBuilder)
    
        migrationBuilder.DropTable(
            name: "UserInfos");
    

然后我修改UserInfo表Type列的类型为字符串类型,再次执行命令

dotnet ef migrations add update_userinfo_type

生成迁移文件如下

public partial class update_userinfo_type : Migration

    protected override void Up(MigrationBuilder migrationBuilder)
    
        migrationBuilder.AlterColumn<string>(
            name: "Type",
            table: "UserInfos",
            nullable: true,
            oldClrType: typeof(int),
            oldType: "integer");
    

    protected override void Down(MigrationBuilder migrationBuilder)
    
        migrationBuilder.AlterColumn<int>(
            name: "Type",
            table: "UserInfos",
            type: "integer",
            nullable: false,
            oldClrType: typeof(string),
            oldNullable: true);
    

这时候我使用命令(dotnet ef migrations script)去生成最后一次的迁移SQL

ALTER TABLE "UserInfos" ALTER COLUMN "Type" TYPE text;
ALTER TABLE "UserInfos" ALTER COLUMN "Type" DROP NOT NULL;
ALTER TABLE "UserInfos" ALTER COLUMN "Type" DROP DEFAULT;

INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20230228131804_update_userinfo_type', '3.1.32');

下面我就开始升级该项目到.Net6版本,并且升级Nuget包到新版本

<ItemGroup>
  <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.1">
    <PrivateAssets>all</PrivateAssets>
    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
  </PackageReference>
  <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.1" />
</ItemGroup>

这个时候我们再使用cli去查看一下最后一次迁移生成的SQL语句

ALTER TABLE "UserInfos" ALTER COLUMN "Type" TYPE text;
ALTER TABLE "UserInfos" ALTER COLUMN "Type" DROP NOT NULL;

INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20230228131804_update_userinfo_type', '7.0.1');

???我文章都写到这里,问题重现不了了?我切换了版本尝试也不行,那算了,我就截图说明一下吧,在我升级之后更新列类型的那一次迁移没有生成SQL

还好我保存了图,早上我可是对比了一下3.x版本生成的更新列类型的迁移文件和7.x版本生成的迁移文件,并且手动修改了迁移文件的内容增加了7.x版本的属性,比如有下面区别

我按照7.x的方式修改这个迁移文件就可以生成更新列的SQL了,然后重新启动项目看到那个表的列已经被修改过来了。

最后

虽然最后没有重现白天的问题,不过以后像升级项目框架的情况中还是要多多测试,多方面考虑,项目中的表结构等修改还是生成SQL脚本去更新数据库靠谱一点,这样子对生成的SQL可控一点。

以上是关于EFCore之项目升级遇到迁移小问题的主要内容,如果未能解决你的问题,请参考以下文章

EFCore生产环境数据库升级方案

EFCore之命令行工具

记录一次EFCore CodeFirst迁移实践,解决多个项目表结构同步更新问题

第四阶段学习总结

第四阶段学习总结

.Net下极限生产力之efcore分表分库全自动化迁移CodeFirst