EF调用FindByNameAsync偶发抛出无法删除正在使用数据库错误
问题描述
基于Entity Framework实现的用户身份认证功能多数时候运行正常,但偶尔执行下述代码行时会抛出删除数据库相关错误:
var user = await UserManager.FindByNameAsync(userName);
目前不清楚该错误的触发原因,也不知道问题出现时的排查方向,核心疑问点为:仅执行单条数据库记录查询操作时,框架为什么会尝试删除数据库?
- UPDATE
根据评论区建议,补充问题相关细节如下:
相关业务代码
OAuth认证逻辑代码片段:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { try { var securityService = GetFromContext<ISecurityService>(context); var logger = GetFromContext<ILogger>(context); var user = await securityService.AuthenticateAsync(context.UserName, context.Password); ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(OAuthDefaults.AuthenticationType); AuthenticationProperties properties = CreateProperties(user); AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties); context.Validated(ticket); context.Request.Context.Authentication.SignIn(oAuthIdentity); } catch (Exception ex) { // 为精简内容省略异常处理逻辑 } }
AuthenticateAsync方法实现:
public async Task<SecureUser> AuthenticateAsync(string userName, string password, bool sendOtp = true) { var user = await GetUserByNameAsync(userName); return user; }
该方法调用最初报错的用户查询方法,相关代码:
public async Task<SecureUser> GetUserByNameAsync(string userName) { var user = await UserManager.FindByNameAsync(userName); return user; }
报错详情
调用上述方法时抛出异常,核心错误信息:
Cannot drop database XXXX because it is currently in use.
出于安全考虑,不公开具体数据库名称。
完整堆栈跟踪:
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async, Int32 timeout, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry) at System.Data.SqlClient.SqlCommand.ExecuteNonQuery() at System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func`3 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed) at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.NonQuery(DbCommand command, DbCommandInterceptionContext interceptionContext) at System.Data.Entity.SqlServer.SqlProviderServices.<>c__DisplayClass57_0.<DropDatabase>b__0(DbConnection conn) at System.Data.Entity.SqlServer.SqlProviderServices.<>c__DisplayClass60_0.<UsingConnection>b__0() at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.<>c__DisplayClass2_0.<Execute>b__0() at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation) at System.Data.Entity.SqlServer.SqlProviderServices.UsingMasterConnection(DbConnection sqlConnection, Action`1 act) at System.Data.Entity.SqlServer.SqlProviderServices.DropDatabase(SqlConnection sqlConnection, Nullable`1 commandTimeout, String databaseName) at System.Data.Entity.SqlServer.SqlProviderServices.DbDeleteDatabase(DbConnection connection, Nullable`1 commandTimeout, StoreItemCollection storeItemCollection) at System.Data.Entity.Database.Delete() at System.Data.Entity.DropCreateDatabaseIfModelChanges`1.InitializeDatabase(TContext context) at System.Data.Entity.Internal.InternalContext.PerformInitializationAction(Action action) at System.Data.Entity.Internal.InternalContext.PerformDatabaseInitialization() at System.Data.Entity.Internal.RetryAction`1.PerformAction(TInput input) at System.Data.Entity.Internal.LazyInternalContext.InitializeDatabaseAction(Action`1 action) at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType) at System.Data.Entity.Internal.Linq.InternalSet`1.Initialize() at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext() at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider() at System.Data.Entity.QueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable`1 source, Expression`1 predicate, CancellationToken cancellationToken) at System.Data.Entity.QueryableExtensions.FirstOrDefaultAsync[TSource](IQueryable`1 source, Expression`1 predicate) at Microsoft.AspNet.Identity.EntityFramework.UserStore`6.<GetUserAggregateAsync>d__67.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() at Rica.Security.SecurityService`1.<GetUserByNameAsync>d__22.MoveNext() in C:\Repos\RICA\Rica.Security\SecurityService.cs:line 301
根因定位
从堆栈里的System.Data.Entity.DropCreateDatabaseIfModelChanges1.InitializeDatabase调用帧可以直接定位根因:当前项目给Entity Framework配置了DropCreateDatabaseIfModelChanges`数据库初始化策略。
这个策略的运行逻辑是:应用启动后第一次访问数据库时,EF会自动比对当前代码里的实体模型和数据库中记录的模型哈希值,一旦检测到两者不匹配,就会直接删除现有数据库,重新创建匹配当前模型的新库再执行初始化种子逻辑。
偶发报错是两个场景刚好撞上:
- 应用重启/应用池回收后,第一个触发数据库访问的请求刚好走到了
FindByNameAsync的查询逻辑,触发了EF的延迟数据库初始化流程 - 删库操作执行时,刚好有其他活跃连接占用着目标数据库(比如其他并发请求、本地打开的数据库客户端连接、运行中的后台任务),EF拿不到数据库的独占权限,就抛出了"数据库正在使用无法删除"的错误。
仅执行查询却触发删库的核心原因是:查询操作触发了EF的首次初始化检查,初始化策略本身带了模型变更自动删库重建的逻辑,和当前执行的是查询还是写入操作没有关系。
注意:
DropCreateDatabaseIfModelChanges、DropCreateDatabaseAlways这类会自动删库的初始化策略,绝对不能用于生产环境,此前已经出现过无数次开发环境配置带到生产导致全量数据被删的事故。
修复方案
按以下步骤处理即可彻底解决问题:
- 生产环境直接禁用EF的自动数据库初始化逻辑,在项目启动配置(比如
Global.asax、Startup.cs)或者对应的IdentityDbContext静态构造函数中加入如下代码:
// 禁用对应DbContext的自动初始化逻辑,彻底杜绝运行时自动删库/建库风险 Database.SetInitializer<你的IdentityDbContext实际类型>(null);
- 开发环境如果需要自动同步模型结构,替换掉删库重建的初始化策略,改用
MigrateDatabaseToLatestVersion初始化器,这个策略通过增量迁移脚本更新数据库结构,不会全量删除重建数据库。 - 规范数据库变更流程:所有实体模型变更对应的数据库脚本,提前在测试环境验证完成,生产环境手动执行迁移脚本,不要依赖EF运行时自动执行结构变更操作。
- 多实例部署场景下,确保所有实例的模型版本和数据库结构完全匹配,避免单实例模型版本不一致触发初始化逻辑。
内容的提问来源于stack exchange,提问作者Kaygee Del Hlobo
相关产品推荐
相关产品推荐

