【问题标题】:Migrating data when adding one-to-one relationship in EF Core?在 EF Core 中添加一对一关系时迁移数据?
【发布时间】:2017-08-06 06:38:12
【问题描述】:

向我的一个表添加新的一对一关系后,我无法弄清楚如何为数据库中的现有行添加默认数据。

我的数据库在升级前基本上是这样的:

-- Team --
  Name: TEXT

-- History --
  Id: INT

... History 所在的位置具有从其他不相关的表中指向它的外键。

在我的升级中,我基本上希望一个团队有一个单一的历史,所以我的新数据库看起来像:

-- Team --
  Name: TEXT
  HistoryId: INT

-- History --
  Id: INT

然而,我现在的问题是我的数据库中有现有的团队,他们需要有唯一的历史记录行来指向,所以我需要为每个现有的团队创建一个新的历史记录行。

我尝试在迁移中手动添加 Up 方法中的条目,但由于我的模型与现有架构不匹配,因此失败。

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.AddColumn<int>(
        name: "HistoryId",
        table: "Team",
        nullable: false,
        defaultValue: 0);

    using (var db = new XMDBContext())
    {
        foreach (var team in db.Team)
            team.History = new XMHistory();
        db.SaveChanges();
    }

    migrationBuilder.CreateIndex(
        name: "IX_Team_HistoryId",
        table: "Team",
        column: "HistoryId",
        unique: true);

    migrationBuilder.AddForeignKey(
        name: "FK_Team_History_HistoryId",
        table: "Team",
        column: "HistoryId",
        principalTable: "History",
        principalColumn: "Id",
        onDelete: ReferentialAction.Cascade);
}

【问题讨论】:

    标签: c# ef-code-first .net-core entity-framework-core


    【解决方案1】:

    不幸的是,目前 EF Core 不支持从迁移中播种数据。该问题在他们的存储库中被跟踪为#629 - Seed Data

    我目前看到的唯一解决方案是通过MigrationBuilder.Sql 方法使用旧的好SQL。不幸的是,无法访问 db 提供程序服务,因此下一个适用于 SQL Server(尽管我尝试仅使用标准 SQL 命令)。

    设原模型如下:

    public class Team
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    
    public class History
    {
        public int Id { get; set; }
    }
    

    将 FK 从 Team 添加到 History 后,变为:

    public class Team
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int HistoryId { get; set; }
        public History History { get; set; }
    }
    

    自动生成的迁移是:

    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.AddColumn<int>(
            name: "HistoryId",
            table: "Team",
            nullable: false,
            defaultValue: 0);
    
        migrationBuilder.CreateIndex(
            name: "IX_Team_HistoryId",
            table: "Team",
            column: "HistoryId");
    
        migrationBuilder.AddForeignKey(
            name: "FK_Team_History_HistoryId",
            table: "Team",
            column: "HistoryId",
            principalTable: "History",
            principalColumn: "Id",
            onDelete: ReferentialAction.Cascade);
    }
    

    现在,在CreateIndex 命令之前,我们手动插入以下内容:

    migrationBuilder.AddColumn<int>(
        name: "TeamId",
        table: "History",
        nullable: true);
    
    migrationBuilder.Sql(@"insert into History (TeamId) select Id from Team");
    
    migrationBuilder.Sql(@"update Team set HistoryId = (select Id from History where TeamId = Team.Id)");
    
    migrationBuilder.DropColumn(
        name: "TeamId",
        table: "History");
    

    这个想法很简单。我们在History 表中创建一个临时可为空的列TeamId,为Team 表中的每条记录插入一条具有对应TeamId 的新记录,然后使用@ 更新Team 表中的HistoryId 列从History表中的987654339@列作为key,最后删除临时列。

    至此数据转换完成,可以创建FK约束了。

    远非良好做法,但可以用作解决方法。

    编辑:Gert Arnold's comments 之后,看起来使用SQL 块是正确的方法。我唯一关心的是如何编写与数据库无关的和/或特定的 SQL。当然,如果一个目标是单一的特定数据库类型,问题就不存在了。无论如何,如果需要处理不同的数据库类型,可以始终使用所有目标数据库支持的标准 SQL 命令,并结合基于 MigrationBuilder.ActiveProvider 属性的特定 if 块。

    【讨论】:

    • 伊万看起来不错!我认为这是唯一的出路。即使有 Seed 方法,它也无济于事,因为它在期间数据库升级时不会运行。这种情况总是需要一些中间数据库状态,因为您无法将所需的 FK 添加到尚不存在的内容中。
    • @GertArnold 我猜你是对的。我正在考虑将迁移拆分为两部分,例如仅向模型添加可为空的属性,生成迁移,种子(用数据填充字段),然后将可空更改为不可空并将导航属性添加到模型,生成第二次迁移。如果在每次迁移结束时运行Seed 方法,所有这一切。但现在我完全不确定这是否可能,因为在迁移过程中实体不会相同。
    • ... 所以可能是 SQL 的方式,只希望有一些信息/帮助器(如 MigrationSqlGenerator 中使用的 ISqlGenerationHelper)以便编写与数据库无关的 SQL。或者直接挂钩MigrationSqlBuilder的方法。因为我们目前只有MigrationBuilder.ActiveProvider 字符串(当然总比没有好:)。
    • 我认为在升级过程中有一个阶段没有一个实体(旧的或新的)代表数据库表(HistoryTeamId),这将始终是一个问题,所以没有任何东西可以用作映射实体(用于生成本机 SQL)。而且两次更改上下文 + 添加迁移和播种似乎有点做作。
    • @Gert 确实。所以只有这样的老派方法 :) 使用一些 SQL 构建助手:)
    猜你喜欢
    • 2023-02-10
    • 2020-03-19
    • 2018-02-10
    • 2021-09-15
    • 2017-05-30
    • 2018-12-03
    • 2020-03-19
    • 2021-07-21
    • 2019-07-25
    相关资源
    最近更新 更多