【问题标题】:Entity Framework telling me the model backing the context has changed实体框架告诉我支持上下文的模型已更改
【发布时间】:2015-04-29 03:11:11
【问题描述】:

我对实体框架代码首次迁移有一个奇怪的问题。几个月来,我一直在项目中使用 EF 和代码优先迁移,并且一切正常。我最近创建了一个新的迁移,并且在运行 Update-Database 我的数据库的恢复备份时,我收到了这个错误:

支持上下文的模型已经改变,因为数据库是 创建的。考虑使用 Code First 迁移来更新数据库

迁移执行如下操作:

public override void Up()
{
    using (SomeDbContext ctx = new SomeDbContext())
    {
        //loop through table and update rows
        foreach (SomeTable table in ctx.SomeTables)
            table.SomeField = DoSomeCalculation(table.SomeField);

        ctx.SaveChanges();
    }
}

我没有使用 Sql() 函数,因为 DoSomeCalculation 必须在 C# 代码中完成。

通常当我得到这样的东西时,意味着我已经以某种方式更新了我的模型并且忘记了创建迁移。然而,这一次并非如此。奇怪的是,我几天前创建的迁移中甚至没有发生错误,并且一直运行良好。

我看了quite 几个articles 关于this,他们似乎都在说电话

Database.SetInitializer<MyContext>(null);

这样做似乎可行,但我的理解(based on this article) 是这样做会消除 EF 确定数据库和模型何时不同步的能力。我不想这样做。我只是想知道为什么它突然认为它们不同步了。

我还尝试运行 Add-Migration 只是为了查看它对模型的看法是否发生了变化,但它不会让我这样做,说明我有待运行的迁移。不错的第 22 条记录,微软。

你猜到这里发生了什么?

我想知道上面列出的迁移使用 EntityFramework 是否是问题所在。似乎可能因为它不再是最新的迁移,所以当 EF 尝试创建一个 SomeDbContext 对象时,它会根据我当前的代码检查数据库(由于我们正在运行迁移,它还不是完全最新的)模型,然后抛出“上下文已更改”错误。

【问题讨论】:

  • 什么版本的 EF,以及您的数据库备份有多长时间(即您希望它应用多少次迁移?)
  • 所有迁移都是 EF 6.1.1-30610。备份中应该有大约 70 个,我的最新代码应该应用另外 3 个。
  • 您的代码同时发生了什么变化?您是否引用了其他库?你的模型和你的程序在同一个程序集中吗?
  • 我确实在模型中添加了一些新字段并生成了新的迁移。就是这样。没有其他重大变化。
  • 嗯...我只是想到了一些事情。我在正在爆炸的迁移中使用 EF。它在尝试创建 DbContext 对象时失败。请记住,迁移不再是最新的迁移。是否有可能当 EF 进行该迁移时,它会根据当前版本的数据库(尚未完全更新,因为我们处于中间或正在运行迁移)检查代码模型的最新版本并抛出这个错误?这可以解释当它是最新的迁移时,它运行良好,现在不再运行。

标签: entity-framework entity-framework-migrations


【解决方案1】:

这可能与您在迁移中使用 EF 有关。我不确定你实际上是如何管理这个的,除非你设置了一个空数据库初始化器。

如果您需要在迁移中更新数据,请使用Sql 函数,例如

Sql("UPDATE SomeTable SET SomeField = 'Blah'");

您应该注意Up() 方法在进行迁移时实际上并未运行,它只是用于设置稍后运行的迁移。因此,尽管您可能认为您已经在使用 EF 的部分进行了迁移,但实际上还没有真正运行。

如果您无法重构计算代码以使其可以用 SQL 编写,那么您将需要使用迁移以外的某种机制来运行此更改。一种可能性是在您的配置中使用Seed 方法,但您需要注意这不会跟踪更改是否已运行。比如……

internal sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
    }

    protected override void Seed(MyContext context)
    {
        // Code here runs any time ANY migration is performed...
    }
}

【讨论】:

  • 在迁移中创建到数据库的常规 ADO.NET SqlConnection 是否有任何问题?我可以这样做来读取表中的所有数据,计算 SomeField 的所有新版本,然后使用 Sql() 用新值更新每一行。
  • 迁移的重点是修改每行的SomeField列内的数据。我不能单独使用 Sql 函数,因为 DoSomeCalculation() 中的东西需要一个 .NET 库。用 T-SQL 是做不到的。
  • 更新了答案,提出了关于使用 Seed 的建议
  • 您能否详细说明一下“您应该注意在进行迁移时 Up() 方法实际上并未运行,它只是用于设置迁移,然后稍后运行。因此,尽管您可能认为您在使用 EF 的位置之上的迁移中做了一些事情,但实际上还没有真正运行。是否有一些文档或我可以查看的内容以了解更多信息?
  • 查看我提出的解决方案。它使用 ADO.NET 并且似乎可以工作,但我担心你的说法是我必须使用除代码优先迁移之外的其他东西。
【解决方案2】:

我尝试用常规的 ADO.NET 代码替换 EntityFramework 代码,它似乎可以工作。这是它的样子:

public override void Up()
{
    Dictionary<long, string> idToNewVal = new Dictionary<long, string>();

    using (SqlConnection conn = new SqlConnection("..."))
    {
        conn.Open();

        using (SqlCommand cmd = new SqlCommand("SELECT SomeID, SomeField FROM SomeTable", conn))
        {
            SqlDataReader reader = cmd.ExecuteReader();

            //loop through all fields, calculating the new value and storing it with the row ID
            while (reader.Read())
            {
                long id = Convert.ToInt64(reader["SomeID"]);
                string initialValue = Convert.ToString(reader["SomeField"]);
                idToNewVal[id] = DoSomeCalculation(initialValue);
            }
        }
    }

    //update each row with the new value
    foreach (long id in idToNewVal.Keys)
    {
        string newVal = idToNewVal[id];
        Sql(string.Format("UPDATE SomeTable SET SomeField = '{0}' WHERE SomeID = {1}", newVal, id));
    }
}

【讨论】:

    猜你喜欢
    • 2014-09-30
    • 1970-01-01
    • 1970-01-01
    • 2013-03-30
    • 1970-01-01
    • 2023-03-23
    • 2013-08-24
    • 1970-01-01
    • 2011-12-17
    相关资源
    最近更新 更多