ChatGPT解决这个技术问题 Extra ChatGPT

在迁移 Up 方法中更改数据 - 实体框架

我在现有模型中添加了一个新属性。它是一个布尔属性,默认值为 true。此表中有现有数据,我想在 Up 方法中创建新字段后立即将特定行的新属性设置为 false。

public override void Up()
{
    AddColumn("dbo.RequestValidationErrors", "IsBreaking",
         c => c.Boolean(nullable: false));
    using (Context ctx = new Context())
    {
        var validation = 
            ctx.RequestValidationErrorSet
            .FirstOrDefault
                (x => x.WordCode == "RequestValidationError.MoreThanOneItemFound");
            if (validation != null)
            {
                validation.IsBreaking = false;
                ctx.SaveChanges();
            }
        }
    }
}

这样 EF 在说的时候会抛出错误

System.InvalidOperationException:支持“DbContext”上下文的模型自创建数据库以来已更改。考虑使用 Code First 迁移来更新数据库

是否可以在这里更改数据库或者我应该在其他地方进行更改?


m
mehrandvd

在迁移过程中,最好使用 Sql() 方法更新数据库数据。

Sql("UPDATE dbo.RequestValidationErrors SET IsBreaking = 0 WHERE WordCode = 'RequestValidationError.MoreThanOneItemFound'");

您还应该为新列定义默认值。所以解决方案应该是这样的:

public override void Up()
{
    AddColumn("dbo.RequestValidationErrors", "IsBreaking", c => c.Boolean(nullable: false, default: true));
    Sql("UPDATE dbo.RequestValidationErrors SET IsBreaking = 0 WHERE WordCode = \"RequestValidationError.MoreThanOneItemFound\"");
}

在迁移过程中使用 DbContext 是非常不明确的。你对上下文有什么期望?它的模型中有迁移后状态,但数据库的表中有迁移前状态。所以模型和数据库不匹配。如果您仍然坚持在代码中使用 DbContext,则禁用模型检查可能是解决方案。您可以使用以下方法禁用模型检查:

Database.SetInitializer<Log4ProContext>(null);

更新:

从 EF Core 2.1 开始,您可以使用 UpdateData 而不是 Sql 方法来处理更简单的情况,就像@ntfrex 在答案中提到的那样:

migrationBuilder.UpdateData(
    table: "RequestValidationErrors", 
    keyColumn: "WordCode", 
    keyValue: "RequestValidationError.MoreThanOneItemFound", 
    column: "IsBreaking", 
    value: false);

我的建议是 NOT TO USE nameof 运算符用于迁移中的表名和列名。因为稍后重命名这些类会导致旧迁移在生产中失败,因为数据库中的表名仍然使用旧名称。


感谢您的解决方案以及缺少的 defaultValue。
当你找到你要找的东西时感觉非常好,你的朋友回答了:)
您是什么意思“最好使用 Sql()”?比使用上下文更好?比使用 UpdateData 方法更好?
@TomasKubes 你是对的。在我撰写此答案时,UpdateData 不可用。我刚刚更新了答案:)
b
bbodenmiller

除了使用 Sql 方法,您还可以使用 UpdateData 方法。

migrationBuilder.UpdateData(
    table: "RequestValidationErrors", 
    keyColumn: "WordCode", 
    keyValue: "RequestValidationError.MoreThanOneItemFound", 
    column: "IsBreaking", 
    value: false);

(不知道是不是只有ef core支持这种方式)


EF Core 从 v2.1 开始支持此功能
这在 ASP.NET Core 3.1 中按预期工作。一个小的改进是尽可能使用nameof(例如nameof(RequestValidationErrors)、nameof(RequestValidationErrors.WordCode)、nameof(RequestValidationErrors,IsBreaking)。
我是 EF Core 的新手,但我认为使用 nameof 不是一个好主意。每次迁移只是关于如何根据数据库的当前状态更新(或降级)模式的描述。这可以递归地应用,以便升级多个模式版本。在迁移中使用 nameof 会将您的迁移逻辑与代码状态耦合,因此可能会失败,因为属性名称的更改现在会影响所有过去使用 nameof(property) 的迁移逻辑。
u
user868386

如果您想使用框架进行此类更改,则应将数据库更改与数据更改分开。

仅为数据库更改创建迁移,然后执行。

然后创建一个新的迁移(Up() 和 Down() 方法将为空)。您现在可以实例化您的 DatabaseContext 并且不会出现错误。通过这种方式,您可以使用框架进行这些更改,并正确实现 Down() 方法。


这个“解决方案”是非常危险的,因为它一开始看起来很有效,但事实并非如此。一旦您创建另一个更改数据库模型的迁移,它就不再起作用,因为您无法创建数据库上下文,因为模型不再是数据库
@NoConnection 由于这个原因,您永远不应该在迁移中使用您的 DbContext 实例。使用 migrationBuilder.Sql.UpdateData 方法迁移您的数据,因为应该没有问题。
C
Chris Moschini

为 EF6 编写 DataMigration 可能是一件苦差事。我们整理了一个库,我只是在这里开源供其他人使用,它为 EF6 添加了这个承诺已久但缺少的功能。只需使用常规 EF 查询等编写类。

https://github.com/b9chris/Brass9.Data