You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 05:04:57