前言
今天遇到了一个很奇怪的问题,这个项目原来的版本是.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可控一点。