如何避免使用自动代码优先迁移重新创建现有数据库

Posted

技术标签:

【中文标题】如何避免使用自动代码优先迁移重新创建现有数据库【英文标题】:How to avoid recreate an existing database using automatic Code First Migration 【发布时间】:2015-04-21 11:58:39 【问题描述】:

我正在使用 EF6,但遇到了数据库自动迁移问题。数据库存在,数据库中没有架构或数据更改。但出于某种奇怪的原因,应用程序似乎试图在机器重新启动时在数据库中重新创建现有表。并因此导致错误。我的问题是:

    为什么在没有数据库架构更改的情况下会自动进行数据库迁移?是什么触发了它? 如何解决。

我用Include方法加载这样的实体,调用的时候报错:

using (var context = new MyDataContext())

    return context.Students.Include("Courses");

Student 类中,我有这个导航属性:

public class Student
    
...

   public virtual ICollection<Course> Courses  get; set;  

在 Configuration.cs 中:

public Configuration()

   this.AutomaticMigrationsEnabled = true;
   this.AutomaticMigrationDataLossAllowed = true;

...

我还在MyDataContext 类中设置了自动数据库迁移,如下所示:

Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyDataContext, MyProject.DataModel.Migrations.Configuration>());

目前,问题是,99% 的情况下,一切正常,但由于某种未知原因,出现以下错误:

System.Data.SqlClient.SqlException (0x80131904):数据库中已经有一个名为“课程”的对象。 在 System.Data.SqlClient.SqlConnection.OnError(SqlException 异常,布尔型 breakConnection,Action1 wrapCloseInAction) at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction) 在 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj,布尔调用者HasConnectionLock,布尔异步关闭) 在 System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior,SqlCommand cmdHandler,SqlDataReader dataStream,BulkCopySimpleResultSet bulkCopyHandler,TdsParserStateObject stateObj,Boolean & dataReady) 在 System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(字符串方法名,布尔异步,Int32 超时,布尔异步写入) 在 System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.ExecuteNonQuery() at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.<NonQuery>b__0(DbCommand t, DbCommandInterceptionContext1 c) 在 System.Data.Entity.Infrastructure.Interception.InternalDispatcher1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func3 操作,TInterceptionContext 拦截上下文,Action3 executing, Action3 执行) 在 System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.NonQuery(DbCommand 命令,DbCommandInterceptionContext 拦截上下文) 在 System.Data.Entity.Internal.InterceptableDbCommand.ExecuteNonQuery() 在 System.Data.Entity.Migrations.DbMigrator.ExecuteSql(DbTransaction 事务,MigrationStatement 迁移语句,DbInterceptionContext 拦截上下文) 在 System.Data.Entity.Migrations.DbMigrator.ExecuteStatementsInternal(IEnumerable1 migrationStatements, DbTransaction transaction, DbInterceptionContext interceptionContext) at System.Data.Entity.Migrations.DbMigrator.ExecuteStatementsInternal(IEnumerable1 migrationStatements,DbConnection 连接) 在 System.Data.Entity.Migrations.DbMigrator.c__DisplayClass30.b__2e() 在 System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.c__DisplayClass1.b__0() 在 System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func1 operation) at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute(Action operation) at System.Data.Entity.Migrations.DbMigrator.ExecuteStatements(IEnumerable1 migrationStatements,DbTransaction existingTransaction) 在 System.Data.Entity.Migrations.DbMigrator.ExecuteStatements(IEnumerable1 migrationStatements) at System.Data.Entity.Migrations.DbMigrator.ExecuteOperations(String migrationId, XDocument targetModel, IEnumerable1 操作,IEnumerable1 systemOperations, Boolean downgrading, Boolean auto) at System.Data.Entity.Migrations.DbMigrator.AutoMigrate(String migrationId, VersionedModel sourceModel, VersionedModel targetModel, Boolean downgrading) at System.Data.Entity.Migrations.DbMigrator.ApplyMigration(DbMigration migration, DbMigration lastMigration) at System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable1 pendingMigrations,字符串 targetMigrationId,字符串 lastMigrationId) 在 System.Data.Entity.Migrations.DbMigrator.UpdateInternal(字符串 targetMigration) 在 System.Data.Entity.Migrations.DbMigrator.c__DisplayClassc.b__b() 在 System.Data.Entity.Migrations.DbMigrator.EnsureDatabaseExists(操作 mustSucceedToKeepDatabase) 在 System.Data.Entity.Migrations.DbMigrator.Update(字符串 targetMigration) 在 System.Data.Entity.MigrateDatabaseToLatestVersion2.InitializeDatabase(TContext context) at System.Data.Entity.Internal.InternalContext.<>c__DisplayClassf1.b__e() 在 System.Data.Entity.Internal.InternalContext.PerformInitializationAction(动作动作) 在 System.Data.Entity.Internal.InternalContext.PerformDatabaseInitialization() 在 System.Data.Entity.Internal.LazyInternalContext.b__4(InternalContext c) 在 System.Data.Entity.Internal.RetryAction1.PerformAction(TInput input) at System.Data.Entity.Internal.LazyInternalContext.InitializeDatabaseAction(Action1 操作) 在 System.Data.Entity.Internal.LazyInternalContext.InitializeDatabase() 在 System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(类型 entityType) 在 System.Data.Entity.Internal.Linq.InternalSet1.Initialize() at System.Data.Entity.Internal.Linq.InternalSet1.Include(字符串路径) 在 System.Data.Entity.Infrastructure.DbQuery`1.Include(字符串路径)

感谢任何帮助! 谢谢!!

【问题讨论】:

【参考方案1】:

如果您正在使用现有数据库,我建议您查看此页面:Code First Migrations with an existing database。在那里,您将看到在对模型进行任何更改之前必须先执行这两个步骤:

    在中运行Add-Migration InitialCreate –IgnoreChanges 命令 包管理器控制台。这将创建一个空迁移 当前模型作为快照。 在包管理器控制台中运行Update-Database 命令。这 将 InitialCreate 迁移应用到数据库。由于 实际迁移不包含任何更改,它只会添加一个 __MigrationHistory 表的行指示此迁移 已经申请了。

当您使用自动迁移运行应用程序时,如果它正在执行任何类型的数据初始化,它将检查__MigrationHistory 表以查看数据库是否需要更新。如果没有__MigrationHistory 表(其中赢了) '不在现有数据库中),模型将不会通过自动迁移针对数据库进行验证。因此,您需要将该表放入现有数据库中。为此,您需要按照我上面描述的步骤进行操作。当您运行Update-Database 命令时,它将在初始迁移中执行Up 方法,这对数据库没有影响。它将根据模型的当前状态在您的数据库中创建__MigrationHistory 表。这样,如果您使用自动迁移,下次 Code First 进行数据库初始化时,它会将当前模型与存储在 __MigrationHistory 中的模型进行比较,以确定数据库是否需要迁移/更新。

【讨论】:

感谢您重新格式化我的帖子。当我说数据库存在时,可能会令人困惑。该数据库由 EF 创建,并且 db 中有一个 __MigrationHistory 表。没有模式,也没有数据模型更改。无需迁移任何内容。问题是:当 .Include("Courses");被调用,抛出异常:System.Data.SqlClient.SqlException (0x80131904): 数据库中已经有一个名为'Courses'的对象。 你好@littlemoli,问题是当数据库已经存在时抛出异常。无论如何尝试运行我建议您建立初始点的步骤【参考方案2】:

如果你做一个技巧,它可能会起作用。

第 1 步:使用包管理器控制台运行带有临时空数据库的 Update_Database。

第 2 步:尝试从临时数据库中获取表 __HistoryMigration 以创建并复制到现有数据库中的表。

【讨论】:

以上是关于如何避免使用自动代码优先迁移重新创建现有数据库的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 .net 核心实体框架迁移更新表(而不是重新创建它们)

如何禁用从未启用的迁移

EF-使用迁移技术让程序自动更新数据库表结构

实体框架 6 代码优先到 RDS 尝试创建数据库

如何从 Laravel 中的现有数据库创建迁移和模型

实体框架代码优先现有数据库和新数据库维护