EF 核心迁移错误:数据库'MyDatabaseName'已存在。选择其他数据库名称



我正在运行ASP。NET Core 2.1和EF Core 2.1应用程序在Windows Server 2016和SQL Server 2017 Web版上运行。

Startup.cs中的public void Configure(IApplicationBuilder app, ...方法结束时,我调用context.Database.Migrate();。这适用于迁移
一切正常。

现在,我在SQL Server 2016的开发环境中备份数据库,将MyDatabaseName.bak文件移动到服务器,并在服务器上恢复数据库MyDatabaseName,然后重新启动IIS站点
当我启动应用程序(打开浏览器)时,我收到以下错误:

应用程序启动异常:系统。数据SqlClient。SqlException(0x80131904):数据库"MyDatabaseName"已存在。选择不同的数据库名称。

行:context.Database.Migrate();。完全错误在底部。

如果我将MyDatabaseName更改为MyDatabaseNameX(它不存在),则创建数据库,应用所有迁移,我可以重置IIS,启动应用程序。如果我恢复数据库,我会得到错误already exists

相同的应用程序(完全相同的dll)在开发和生产环境中运行应用程序。这也意味着数据库结构是相同的。

我需要在生产中恢复数据库。我只是不确定context.Database.Migrate()为什么会抛出错误?

完全错误:

应用程序启动异常:系统。数据SqlClient。SqlException(0x80131904):数据库"MyDatabaseName"已存在。选择不同的数据库名称。在系统数据SqlClient。SqlConnection。OnError(SqlException异常,布尔breakConnection,操作1 wrapCloseInAction) at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction)系统数据SqlClient。TdsParser。ThrowExceptionAndWarning(TdsParserStateObjectstateObj、布尔调用程序HasConnectionLock、布尔异步关闭)系统数据SqlClient。TdsParser。TryRun(RunBehavior RunBehavior,SqlCommand cmdHandler,SqlDataReader数据流,BulkCopySimpleResultSet bulkCopyHandler,TdsParserStateObjectstateObj,布尔&dataReady)系统数据SqlClient。SqlCommand。RunExecuteNonQueryTds(字符串methodName,布尔异步,Int32超时,布尔异步写入)系统数据SqlClient。SqlCommand。InternalExecuteNonQuery(任务完成源1 completion, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite, String methodName) at System.Data.SqlClient.SqlCommand.ExecuteNonQuery() at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConnection connection, DbCommandMethod executeMethod, IReadOnlyDictionary2parameterValues)微软EntityFrameworkCore。存储内部的相关命令。ExecuteNonQuery(IRelationConnection连接,IReadOnlyDictionary2 parameterValues) at Microsoft.EntityFrameworkCore.Migrations.MigrationCommand.ExecuteNonQuery(IRelationalConnection connection, IReadOnlyDictionary2个参数值)微软EntityFrameworkCore。迁移。内部的MigrationCommandExecutor。ExecuteNonQuery(IEnumerable`1migrationCommands,IRelationConnection连接)微软EntityFrameworkCore。SqlServer。存储内部的SqlServerDatabaseCreator。创建()在微软EntityFrameworkCore。迁移。内部的Migrator。迁移(字符串targetMigration)微软EntityFrameworkCore。RelationalDatabaseFacadeExtensions。迁移(DatabaseFacadedatabaseFacade)。启动。配置(IApplicationBuilder应用程序、AppUserManager userManager、IServiceProvider serviceProvider)C: \GitLab Runner\builds\7cab42e4\0\web\MyProject\Startup.cs:line 582---从上一个引发异常的位置开始的堆栈结尾跟踪--在微软AspNetCore。主机。ConventionBasedStartup。配置(IApplicationBuilder应用程序)微软AspNetCore。服务器IIS集成。IIS设置筛选器<>c__DisplayClass4_0.b_0(IApplicationBuilder应用程序)微软AspNetCore。主机。内部的自动请求服务启动筛选器<>c__DisplayClass0_0.b_0(IApplicationBuilder建筑商)微软AspNetCore。主机。内部的WebHost。生成应用程序()ClientConnectionId:7f6b84a3-e0ea-42c7-947d-a9cafdafbffa错误编号:1801,州:3,班级:16托管环境:制作内容根路径:C:\WWW\MyProject正在侦听:http://127.0.0.1:24830应用程序已启动。按Ctrl+C关闭。应用程序是正在关闭。。。

这是一个令人讨厌的问题。数据库确实存在(我确实恢复了它),但问题是数据库的备份所有者也被转移了
作为localhost所有者的用户在服务器上不存在。所以迁移并没有找到数据库(因为它无法访问),所以它尝试创建一个新的数据库。

在开始讨论可能的解决方案之前,我们需要了解一件重要的事情:迁移模式是确保您正在处理的所有数据库(以及您将用于连接应用程序的数据库)在任何给定环境中(测试、阶段、生产、灾难恢复等)都具有一致且最新的结构的绝佳方法;如果你选择使用它,你能做的最好的事情就是坚持模式的最佳实践,并确保Migrate()方法在你需要的时候被调用。

也就是说,您可能仍然希望在第一次运行时仅使用Migrate()方法来创建数据库,而不必通过编程(自动)跟踪任何进一步的迁移。如果这就是你所处的场景,那么你能做的最好的事情就是将Migrate()方法封装在一个条件块中,比如下面的块:

if (!dbContext.Database.GetService<IRelationalDatabaseCreator>().Exists())
{
var host = BuildWebHost(args);
using (var scope = host.Services.CreateScope())
{
var dbContext = scope.ServiceProvider.GetService<ApplicationDbContext>();
var roleManager = scope.ServiceProvider.GetService<RoleManager<IdentityRole>>();
var userManager = scope.ServiceProvider.GetService<UserManager<ApplicationUser>>();
// Create the Db if it doesn't exist and applies any pending migration.
dbContext.Database.Migrate();
// Seed the Db
DbSeeder.Seed(dbContext, roleManager, userManager);
}
host.Run();
}

这样,您将确保只有当数据库还不存在时,Migrate()方法才会以编程方式执行:这将非常适合测试环境,在测试环境中,您可以每次删除并重新创建数据库,而不必担心丢失实际数据,和/或您喜欢手动更新数据库的任何其他上下文,例如使用dotnet efpowershell命令。这对性能有好处,也避免了Migrate()方法中的SqlException,因为它只会在没有数据库的情况下运行,从而防止发现错误或过时的迁移数据。

如果你想知道Exists()方法在幕后到底做了什么,您可以通过查看EF Core的官方GitHub存储库中的SqlServer.Storage/Internal/SqlServerDatabaseCreator.cs类来轻松地检查它:您会发现没有真正的魔力——只是尝试打开连接,然后捕获SqlException并返回false或返回true。可能不是你希望在那里找到的最好的东西,但总比什么都没有好(至少它能起到作用)。

如果你需要更多的信息,请查看我写的关于这个问题的博客文章。

一种可能性是数据库是由其他东西/早期迁移创建的。

如果数据库在列表中(不应该在列表中),您可以通过在Sql server management studio中查看来验证是否存在这种情况。然后尝试创建具有相同名称的数据库。如果你再次出现错误,那是因为它已经被创建了。

至于解决方案,您可以简单地等到数据库存在,这样您就可以再次迁移,但这并不总是实用的。另一种方法是尝试捕获此异常并添加重试机制。

try 
{
// migrate
}
catch (SqlException exception) when (exception.Number == 1801)
{
// retry
}

IIS APPPOLL用户添加到数据库MyDatabaseName的用户列表

最新更新