为何执行db.savechanges时会保存前序登录用户的旧偏好记录?
问题解决方案
根因分析
你遇到的两个问题由两个关联错误共同导致:
- DbContext生命周期管理错误:你将
ApplicationDbContext声明为类级别的全局字段,默认会在控制器实例存活期间一直复用同一个上下文实例。Web API控制器的生命周期默认是按请求,但如果有错误注入或者异常导致控制器未被释放,上下文会一直缓存之前请求追踪的所有实体改动,调用SaveChanges()时会把所有未提交的改动(包括上一个用户的偏好改动)一并写入数据库,直接导致跨用户数据串扰。 - 偏好查询匹配逻辑错误:你在查询用户偏好时仅对传入的
model.Key做了Trim处理,没有对数据库存储的u.Key做Trim匹配,如果数据库中存储的Key前后带有空格,会导致明明存在对应偏好却匹配失败,userPreferences返回null,直接走到else分支添加新配置,进一步触发上下文残留实体的提交。
修复步骤
- 第一步:修正DbContext生命周期,避免跨请求实体追踪
不要使用类级别的全局DbContext实例,改为方法内创建,或者通过依赖注入配置为按请求注入,每次请求使用全新的上下文实例,请求结束自动释放:// 移除类级别的private ApplicationDbContext db = new ApplicationDbContext(); public IHttpActionResult Add(UserPreferencesDto model) { // 方法内创建上下文,用using保证执行完自动释放 using var db = new ApplicationDbContext(); // 原有逻辑 } - 第二步:修正偏好查询匹配逻辑,解决存在配置却走到else分支的问题
查询时对数据库端的Key也做Trim处理,保证前后空格不影响匹配:
建议同时在新增偏好时也对Key做Trim后再存入数据库,从根源避免Key带空格的问题。var userPreferences = db.UserPreferences.Where(u => u.UserId == model.UserId && u.Key.Trim() == model.Key.Trim()) .FirstOrDefault(); - 第三步:补充身份校验(可选优化)
方法开头增加登录状态校验,避免拿到空UserId写入脏数据:if (!User.Identity.IsAuthenticated) { return Unauthorized(); } model.UserId = User.Identity.GetUserId(); - 临时兼容方案(如果暂时无法修改全局DbContext声明)
可以在每次操作前手动清除上下文追踪的所有用户偏好实体,避免残留改动被提交:// 方法开头先清除已追踪的实体 var trackedPrefs = db.ChangeTracker.Entries<UserPreferences>().ToList(); foreach (var entry in trackedPrefs) { entry.State = EntityState.Detached; }
内容的提问来源于stack exchange,提问作者Ivan
相关产品推荐
相关产品推荐

