前言
今天遇到了一個很奇怪的問題,這個項目原來的版本是.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>(able: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Name = table.Column<string>(able: true),
Type = table.Column<int>(able: 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",
able: true,
oldClrType: typeof(int),
oldType: "integer");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<int>(
name: "Type",
table: "UserInfos",
type: "integer",
able: false,
oldClrType: typeof(string),
oldable: true);
}
}
這時候我使用指令(dotnet ef migrations script)去生成最後一次的遷移SQL
ALTER TABLE "UserInfos" ALTER COLUMN "Type" TYPE text;
ALTER TABLE "UserInfos" ALTER COLUMN "Type" DROP NOT ;
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 ;
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可控一點。