如何在EF迁移Up方法中用DbContext迁移JSON列数据至关联表
迁移JSON列数据到关联表的实现步骤
针对你的PSQL + EF Code-First场景,自定义Migration的Up方法完成数据迁移可以按以下步骤操作:
1. 生成初始迁移文件
先执行迁移命令生成基础结构变更的迁移文件:
# Package Manager Console Add-Migration SplitCirclesToSeparateTable # 或者.NET CLI dotnet ef migrations add SplitCirclesToSeparateTable
2. 修改迁移文件的Up方法
打开生成的迁移文件,在自动生成的代码中间插入数据迁移逻辑,完整的Up方法示例如下:
using Microsoft.EntityFrameworkCore.Migrations; using Newtonsoft.Json; using System.Collections.Generic; using System.Linq; #nullable disable namespace YourNamespace.Migrations { public partial class SplitCirclesToSeparateTable : Migration { protected override void Up(MigrationBuilder migrationBuilder) { // 1. 保留自动生成的创建Circles表代码 migrationBuilder.CreateTable( name: "Circles", columns: table => new { Id = table.Column<int>(type: "integer", nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), Radius = table.Column<int>(type: "integer", nullable: false), IdFigure = table.Column<int>(type: "integer", nullable: false) }, constraints: table => { table.PrimaryKey("PK_Circles", x => x.Id); table.ForeignKey( name: "FK_Circles_Figures_IdFigure", column: x => x.IdFigure, principalTable: "Figures", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); // 2. 新增:从旧列读取数据并写入新表 using (var context = new GeometryDbContext(this.ContextOptions)) { // 用原始SQL读取旧表的Id和Circles序列化字符串 var figureData = context.Database.SqlQuery<FigureCirclesDto>( "SELECT Id, Circles FROM Figures").ToList(); foreach (var data in figureData) { if (!string.IsNullOrWhiteSpace(data.Circles)) { // 反序列化JSON为Circle列表 var circles = JsonConvert.DeserializeObject<List<Circle>>(data.Circles) ?? new List<Circle>(); foreach (var circle in circles) { circle.IdFigure = data.Id; context.Circles.Add(circle); } } } context.SaveChanges(); } // 3. 保留自动生成的删除旧Circles列代码 migrationBuilder.DropColumn( name: "Circles", table: "Figures"); } protected override void Down(MigrationBuilder migrationBuilder) { // 回滚逻辑:重新添加Circles列,将新表数据序列化回旧列 migrationBuilder.AddColumn<string>( name: "Circles", table: "Figures", type: "text", nullable: true); using (var context = new GeometryDbContext(this.ContextOptions)) { var figures = context.Figures.Include(f => f.Circles).ToList(); foreach (var figure in figures) { var circles = context.Circles.Where(c => c.IdFigure == figure.Id).ToList(); context.Entry(figure).Property("Circles").CurrentValue = JsonConvert.SerializeObject(circles); } context.SaveChanges(); } migrationBuilder.DropTable( name: "Circles"); } // 接收原始SQL查询结果的私有DTO private class FigureCirclesDto { public int Id { get; set; } public string Circles { get; set; } } } }
关键说明
- 复用迁移配置:迁移类自带
ContextOptions属性,直接用它初始化GeometryDbContext,确保和迁移使用相同的数据库连接配置。 - 原始SQL读取旧数据:修改后的实体类中
Figure.Circles已改为导航属性,无法直接通过EF实体读取旧JSON列,因此用SqlQuery直接从数据库获取原始列数据。 - 大数据量优化(可选):如果
Figures表数据量极大,建议用Skip+Take分批读取插入,避免内存溢出。
3. 执行迁移
修改完成后,执行命令更新数据库:
# Package Manager Console Update-Database # 或者.NET CLI dotnet ef database update
内容的提问来源于stack exchange,提问作者Federico Ruocco
相关产品推荐
相关产品推荐

