在EF Core中添加一对一关系时迁移数据?

时间:2017-03-15 13:27:43

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

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

我的数据库在升级之前基本上看起来像这样:

-- 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);
}

1 个答案:

答案 0 :(得分:5)

不幸的是,目前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; }
}

Team的FK添加到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");

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

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

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

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