自创建数据库以来,支持<Database>上下文的模型已更改。

265

错误信息如下:

"自数据库创建以来,'AddressBook'上下文所支持的模型已更改。请手动删除/更新数据库,或使用IDatabaseInitializer实例调用Database.SetInitializer。例如,RecreateDatabaseIfModelChanges策略将自动删除和重新创建数据库,并可选择性地填充新数据。"

我正在尝试使用Code-First功能,以下是我编写的内容:

var modelBuilder = new ModelBuilder();
var model = modelBuilder.CreateModel();
using (AddressBook context = new AddressBook(model))
{
    var contact = new Contact
    {
        ContactID = 10000,
        FirstName = "Brian",
        LastName = "Lara",
        ModifiedDate = DateTime.Now,
        AddDate = DateTime.Now,
        Title = "Mr."

    };
    context.contacts.Add(contact);
    int result = context.SaveChanges();
    Console.WriteLine("Result :- "+ result.ToString());
}
上下文类:
public class AddressBook : DbContext
{
    public AddressBook()
    { }
    public AddressBook(DbModel AddressBook)
        : base(AddressBook)
    {

    }
    public DbSet<Contact> contacts { get; set; }
    public DbSet<Address> Addresses { get; set; }
}

以及连接字符串:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <connectionStrings>
    <add name="AddressBook" providerName="System.Data.SqlClient"  
         connectionString="Data Source=MyMachine;Initial Catalog=AddressBook;
         Integrated Security=True;MultipleActiveResultSets=True;"/>
    </connectionStrings>
</configuration>

所以,数据库名称是“AddressBook”,当我尝试将联系人对象添加到上下文时发生错误。这里有什么遗漏吗?


1
从您的数据库中删除 __MigrationHistory 表。 - Zahid Hasan
1
@ZahidHasan 绝对不要删除 __MigrationsHistory 表,除非您完全理解 __MigrationsHistory 的目的,并愿意手动协调代码和数据库之间的差异,并在生产环境中进行相同的更改。 - Aaron Queenan
27个回答

420

现在是:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    Database.SetInitializer<YourDbContext>(null);
    base.OnModelCreating(modelBuilder);
}

在你的 YourDbContext.cs 文件中。


13
附注:这个代码需要放在Global.asax文件的Application_Start()方法中。 - BritishDeveloper
50
比Global.asax更好的方法是将此代码放置在您的DbContext类的构造函数中。这样,它就可以为使用该上下文的每个站点工作,而不仅仅是由Global.asax文件控制的一个站点。 - Corin
7
最好将其放在上下文类的静态构造函数中,这样它只会被调用一次,就像这个示例视频中所示:http://msdn.microsoft.com/en-us/data/jj572367。 - Christian Fredh
3
应该将其放在受保护的重写方法 void OnModelCreating(DbModelBuilder modelBuilder) 内部: { Database.SetInitializer<YourDbContext>(null); base.OnModelCreating(modelBuilder); } 这将取消数据库初始化并调用基类上的 OnModelCreating 方法。 - Chris Voon
@Toolkit 这个问题和答案是在 EF Code First 出现多年之前编写的。 - Matt Frear
显示剩余9条评论

140

以下是来自Scott Gu的博客中由Jeff发布的一些信息,以解释到底发生了什么:

对于那些看到这个异常的人:

"The model backing the 'Production' context has changed since the database was created. Either manually delete/update the database, or call Database.SetInitializer with an IDatabaseInitializer instance."

以下是情况分析和解决方法:

当模型首次创建时,我们会运行一个 DatabaseInitializer ,用于执行一些操作,例如在不存在数据库时创建数据库或添加种子数据。默认的 DatabaseInitializer 会尝试将需要使用模型的数据库模式与存储在 EdmMetadata 表中的模式哈希值进行比较(当 Code First 创建数据库时会创建该表)。现有的数据库不会有 EdmMetadata 表,因此也就没有哈希值,如果缺少该表,则当前实现会抛出异常。我们将在正式版发布之前改变此行为。在此之前,现有的数据库通常不需要任何数据库初始化程序,因此可以通过调用以下内容关闭上下文类型的初始化程序:

Database.SetInitializer<YourDbContext>(null);

杰夫


9
我今天尝试了这个,不再出现“模型已更改”的错误,取而代之的是“dbo.Table对象名无效”。 - Stefan Bergfeldt
3
杰夫希望这是一种解决方法,但已经过去两年多了,仍然需要将SetInitializer设置为null。对吧?那么,有人能够解释一下这如何适用于迁移工作流程吗? - kroiz
4
我也使用EF6,但如果将它放在Global.asax里,只能修复网站运行时的问题。如果有单元测试,你就无法处理了。最好将其放在YourDbContext的构造函数中。这样可以修复每个项目(包括网站和测试项目)的问题。 - Rap
1
在我看来,这个答案应该得到更高的分数,因为它实际上解释了为什么我们需要添加这行代码。谢谢。 - Paul
1
如果您或其他人遇到“Invalid object name 'dbo.Table”错误,请检查您的连接字符串attachDbFilename和initial catalog。 - benscabbia
显示剩余3条评论

44

对于 Entity Framework 5.0.0.0 - 6.1.3,您确实需要执行以下操作:

1. using System.Data.Entity;   to startup file (console app --> Program.cs / mvc --> global.asax
2. Database.SetInitializer<YourDatabaseContext>(null);

是的,Matt Frear是正确的。更新-编辑:但我同意其他人的观点,不应该将此代码添加到global.asax中,而应该添加到您的DbContext类中。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // other code 
    Database.SetInitializer<YOURContext>(null);
    // more code here.
}

正如其他人提到的,这对处理单元测试也很好。

目前我正在使用它与Entity Framework 6.1.3 /.net 4.6.1一起使用

我将在不久的将来回来提供CORE代码片段。


1
谢谢!Program.cs 绝对适用于控制台。 - JsAndDotNet
但是,当您第一次初始化数据库时,如果我在onModelCreating方法中设置setinitializer为null,则不会创建数据库。有什么想法吗?即使我使用以下代码:using (var context = Activator.CreateInstance()) { context.Database.Initialize(true); } - Rupesh Kumar Tiwari
我需要找到我曾经注释掉一行并交换的代码...我不记得问题是什么,我需要查看。 - Tom Stickel
1
最佳解决方案。它使我的解决方案运行,但我不知道后果是什么。提交并部署。 - Svend

37

只需在 SQL Server Management Studio 中运行以下 SQL 命令:

delete FROM [dbo].[__MigrationHistory]

4
你救了我的命!谢谢。 - Marek Dorda
4
除非您完全理解__MigrationsHistory表的目的并愿意手动协调代码和数据库之间的差异,并在生产环境中进行相同的更改,否则请勿删除__MigrationsHistory表。 - Aaron Queenan

31

在 CTP5 之后,这个修复方法已经不再适用。

您需要执行 Database.SetInitializer<YourContext>(null);


1
那应该放在哪里呢... OnModelCreating 没有任何可访问的名为 DbDatabase 的内容。 - James Reategui
在启动过程中,我将我的设置放在Application_Start中。 - chrisortman
Database.SetInitializer 在 EF 4.3 正式版中运行良好。 - Richard Beier
1
我认为,“此修复程序在 CTP5 后不再起作用”是指他所说的是来自 2010 年 8 月 30 日的已接受答案。 - Tom Stickel

18

刚刚找到答案,想在这里更新一下。只需要按照以下步骤操作即可。

public class AddressBook: DbContext
{
   protected override void OnModelCreating(ModelBuilder modelBuilder)
   {
    modelBuilder.IncludeMetadataInDatabase = false;
   }
}

13
使用EF的较新版本已不再支持此功能,同时modelBuilder.Conventions.Remove<IncludeMetadataConvention>();也无法解决这个问题。但是使用DbDatabase.SetInitializer(null);可以解决。 - JTew
@TomStickel - 我同意。将 https://dev59.com/t3A65IYBdhLWcg3w2iep#6143116 标记为答案。 - Ashish Gupta

16

或者您可以将此行放在Global.asax.cs文件中的Application_Start()下:

System.Data.Entity.Database.SetInitializer(new System.Data.Entity.DropCreateDatabaseIfModelChanges<ProjectName.Path.Context>());

请确保将ProjectName.Path.Context更改为您的命名空间和上下文。如果使用代码优先,则会在架构发生更改时删除并创建新的数据库。


正是我所需要的,因为我只是在做原型设计。非常感谢。 - Learner

8
我花了很多天时间来解决这个问题,分析了许多不同的帖子并尝试了许多选项,最终解决了问题。我的解决方案中有两个项目使用EF代码优先迁移:
  • 控制台应用程序“DataModel”,主要用作包含所有我代码优先实体、DbContext、Mirgations和通用存储库的程序集。我已将单独的空本地数据库文件(位于DataModel / App_Data文件夹中)包含在此项目中,以便能够从程序包管理器控制台生成迁移。
  • WebApi,它引用DataModel项目并使用WebApi / App_Data文件夹中未包含在项目中的本地数据库文件

当请求WebApi时,我遇到了这个错误...

我的环境:

  • Windows 8.1 x64
  • Visual Studio 2015 Professional with Update 1
  • 我所有的项目都针对.NET Framework 4.6.1
  • 从NuGet获取的EntityFramework 6.1.3

这里我收集了您应该注意的所有备注以及必须满足的所有条件/要求,以避免出现上述异常:

  1. You should use only one version of EntityFramework Nuget package for all projects in your solution.
  2. Database, created by running sequentially all migration scripts should have the same structure/schema as you target database and correspond to entity model. Following 3 things must exactly correspond/reflect/match each other:
    • Your all migration script up to last
    • Current code first entity model state (DbContext, entities)
    • Target database
  3. Target database (mdf file) should be updated/correspond up to last migration script. Verify that "__MigrationHistory" table in your target database contains records for all migration scripts that you have, it means that all migration scripts was successfully applied to that database. I recommend you to use Visual Studio for generation correct code first entities and context that corresponds to your database, Project -> Add New Item -> ADO.NET Entity Data Model -> Code First from database: Of course, as an alternative, if you have no database you can write manually model (code first entities and context) and then generate initial migration and database.
  4. Name of connection string e.g. MyConnectionString in config file of startup project (Web.config/App.config):

    <configuration>
      <connectionStrings>
        <add name="MyConnectionString" connectionString="...">
      </connectionStrings>
    <configuration>
    

    should be equal to parameter passed in constructor of your DbContext:

     public partial class MyDbContext : DbContext
     {
        public MyDbContext()
           : base("name=MyConnectionString"){}
        ...
    
  5. Before using Package Manager Console, make sure that you are using correct database for update or generate migration and needed project is set as startup project of solution. For connect to database it will use connection string from that .config file, which in project, that is set as startup project.
  6. And the main, which fixed my issue: It is weird, but in my WebApi/bin folder DataModel.exe was old, not refreshed since last build. Since migrations was embedded in my assembly DataModel.exe then my WebApi updated database using old mirgations. I was confused why after updating database in WebApi it not corresponds to latest migration script from DataModel. Following code automatically creates(if not exists) or updates to latest migration local database in my WebApi/App_Data folder.

       public class WebApiApplication : System.Web.HttpApplication
       {
           protected void Application_Start()
           {
               Database.SetInitializer(new MigrateDatabaseToLatestVersion<ODS_DbContext, Configuration>()); 
               ...
    

    I tried clean and rebuild solution but it did not help, than I completely removed bin and obj folders from WebApi, deleted database files from WebApi/App_Data, built, restarted WebApi, made request to it, it created correct database - lazy initialization (using lines above), which corresponds to latest migration and exception didn't appear more. So, this may fix your problem:

    1. remove manually bin, obj folders from your startup project (which generates/updates your database)
    2. build your startup project or better clean and rebuild all you solution.
    3. recreate database by starting project (will execute lines above) or use Package Manager Console "update-database" command.
    4. manually check whether generated db and __MirgationHistory corresponds to latest migration script.

5
对于我来说,升级到4.3.1版本后,我只需截断EdmMetaData表或直接将其删除。

我升级到了4.3.1,然后只是重命名了EdmMaetaData表。现在我可以根据需要更改模型,不再出现关于模型支持的烦人错误消息。 - Ashok Padmanabhan

3

对于VB.NET开发人员:

在Global.asax.vb文件的Application_Start()方法末尾添加以下行:

Database.SetInitializer(Of ApplicationDbContext)(Nothing)

将ApplicationDbContext更改为您特定的Db上下文。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接