ASP.NET Core 3.1 MVC迁移更新数据库遇AspNetRoles已存在错误求助
场景说明
使用ASP.NET Core 3.1 MVC框架、SQL Server数据库,更新旧模型并添加新模型后,执行以下命令尝试更新数据库:
Add-Migration NameMigration
随后执行:
Update-Database
持续收到错误提示数据库中已存在AspNetRoles对象。
错误信息
生成已开始...
生成成功。失败: Microsoft.EntityFrameworkCore.Database.Command[20102]
执行DbCommand失败(耗时99毫秒) [参数=[], 命令类型='Text', 命令超时='30']
CREATE TABLE [AspNetRoles] (
[Id] nvarchar(450) NOT NULL,
[Name] nvarchar(256) NULL,
[NormalizedName] nvarchar(256) NULL,
[ConcurrencyStamp] nvarchar(max) NULL,
CONSTRAINT [PK_AspNetRoles] PRIMARY KEY ([Id])
);执行DbCommand失败(耗时99毫秒) [参数=[], 命令类型='Text', 命令超时='30']
CREATE TABLE [AspNetRoles] (
[Id] nvarchar(450) NOT NULL,
[Name] nvarchar(256) NULL,
[NormalizedName] nvarchar(256) NULL,
[ConcurrencyStamp] nvarchar(max) NULL,
CONSTRAINT [PK_AspNetRoles] PRIMARY KEY ([Id])
);
Microsoft.Data.SqlClient.SqlException (0x80131904): 数据库中已存在名为'AspNetRoles'的对象。
at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction) at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction)
at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at Microsoft.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean isAsync, Int32 timeout, Boolean asyncWrite)
at Microsoft.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource1 completion, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry, String methodName) at Microsoft.Data.SqlClient.SqlCommand.ExecuteNonQuery() at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteNonQuery(RelationalCommandParameterObject parameterObject) at Microsoft.EntityFrameworkCore.Migrations.MigrationCommand.ExecuteNonQuery(IRelationalConnection connection, IReadOnlyDictionary2 parameterValues)
at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationCommandExecutor.ExecuteNonQuery(IEnumerable`1 migrationCommands, IRelationalConnection connection)
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_0.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)客户端连接ID:e6b96a6c-817f-4d97-9fac-d1deefdfde2c
错误编号:2714,状态:6,级别:16
数据库中已存在名为'AspNetRoles'的对象。
相关代码
Program.cs
public class Program { public static async Task Main(string[] args) { var host = CreateHostBuilder(args).Build(); using (var scope = host.Services.CreateScope()) { var services = scope.ServiceProvider; try { var db = scope.ServiceProvider.GetRequiredService<ApplicationContext>(); db.Database.Migrate(); var userManager = services.GetRequiredService<UserManager<User>>(); var rolesManager = services.GetRequiredService<RoleManager<IdentityRole>>(); await RoleInitializer.InitializeAsync(userManager, rolesManager); var context = services.GetRequiredService<ApplicationContext>(); await BasicInformationInitialize.InitializeLCAsync(context); } catch (Exception ex) { var logger = services.GetRequiredService<ILogger<Program>>(); logger.LogError(ex, "An error occurred while seeding the database."); } } host.Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); }
ApplicationContext.cs
public class ApplicationContext : IdentityDbContext<User> { //code public DbSet<Model> TabelName { get; set; } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); //code } public ApplicationContext(DbContextOptions<ApplicationContext> options) : base(options) { Database.EnsureCreated(); } }
解决方案
1. 移除冲突的数据库初始化代码
问题根源是ApplicationContext构造函数中的Database.EnsureCreated()与迁移机制冲突:
EnsureCreated会直接创建所有模型对应的表(包括Identity的AspNetRoles等),但不会在__EFMigrationsHistory表中记录迁移历史。- 后续执行
Update-Database时,EF认为这些表尚未通过迁移创建,会重复执行创建语句,导致报错。
修改ApplicationContext构造函数,删除Database.EnsureCreated():
public ApplicationContext(DbContextOptions<ApplicationContext> options) : base(options) { // 移除 Database.EnsureCreated(); }
2. 同步迁移历史与现有数据库
根据数据是否需要保留,选择以下两种方案:
方案A:无需保留现有数据(简单快速)
- 直接删除SQL Server中的目标数据库。
- 重新执行
Update-Database,EF会从头应用所有迁移,自动创建包括Identity表在内的所有结构,并记录迁移历史。
方案B:需要保留现有数据
- 打开SQL Server管理工具,找到目标数据库的
__EFMigrationsHistory表。 - 将初始迁移(第一次执行
Add-Migration生成的迁移类对应的MigrationId和ProductVersion)插入到该表中。例如初始迁移名为20240520120000_InitialCreate,则插入:
(注意替换为你实际的MigrationId和ASP.NET Core EF版本)INSERT INTO __EFMigrationsHistory (MigrationId, ProductVersion) VALUES ('20240520120000_InitialCreate', '3.1.32'); - 执行完成后,再运行
Update-Database,EF会识别到Identity表已通过初始迁移创建,只会应用后续的新迁移。
3. 验证执行
修改代码并同步迁移历史后,重新运行Update-Database,即可完成数据库更新。
内容的提问来源于stack exchange,提问作者Artem

