使用await修饰_context.Entry(job)报错及乐观并发问题求助
问题解答
1. 关于_context.Entry(job)无法使用await的问题
DbContext.Entry()是同步方法,直接返回DbEntityEntry<Job>对象,并不返回Task类型,因此完全不需要(也不能)用await修饰。直接移除await即可解决错误:
var entry = _context.Entry(job); // 正确写法,无需await
2. 乐观并发错误的解决方案
你遇到的DbUpdateConcurrencyException和OnBeforeSave是否用await无关,本质是:从ViewModel映射得到的job对象中**并发令牌(TimeStamp)**与数据库当前存储的版本不一致。以下是具体修复步骤:
步骤1:确认并发令牌配置正确
确保Job实体的TimeStamp属性已配置为EF的并发令牌,比如用数据注解:
public class Job { // 其他属性... [Timestamp] public byte[] TimeStamp { get; set; } }
或Fluent API:
modelBuilder.Entity<Job>() .Property(j => j.TimeStamp) .IsRowVersion();
步骤2:更新前获取数据库最新的并发令牌
因为你直接从ViewModel映射job并Attach到上下文,这个job的TimeStamp是前端提交的旧值,和数据库最新版本不匹配,导致并发校验失败。可以通过两种方式解决:
方案A:仅获取最新TimeStamp
// 在Attach前,查询数据库获取当前job的TimeStamp var currentJobTimeStamp = await _context.Jobs .Where(j => j.Id == command.Id) .Select(j => j.TimeStamp) .FirstOrDefaultAsync(cancellationToken); // 将最新TimeStamp赋值给映射后的job job.TimeStamp = currentJobTimeStamp; // 执行后续逻辑 OnBeforeAttach(job); _context.Set<DataAccess.Domain.Lab.Job>().Attach(job); OnBeforeSave(job); // 此方法无异步操作,无需await await _context.SaveChangesAsync(cancellationToken);
方案B:加载完整实体并更新(更稳妥)
这种方式由EF自动跟踪属性变化,无需手动标记修改:
// 从数据库加载最新的job实体 var existingJob = await _context.Jobs.FindAsync(new object[] { command.Id }, cancellationToken); if (existingJob == null) { throw new KeyNotFoundException($"Job with Id {command.Id} not found."); } // 将ViewModel属性映射到现有实体 _mapper.Map(jobViewModel, existingJob); // 直接保存,EF自动处理并发校验 await _context.SaveChangesAsync(cancellationToken); return jobViewModel;
步骤3:调整OnBeforeSave逻辑(若坚持用Attach方式)
该方法内无异步操作,无需定义为async Task,同时确保并发令牌的修改标记正确:
private void OnBeforeSave(Job job) { var entry = _context.Entry(job); if (job.IsNew) { entry.State = EntityState.Added; } else { entry.State = EntityState.Unchanged; // 标记需要更新的属性 entry.Property(j => j.Name).IsModified = true; entry.Property(j => j.Description).IsModified = true; entry.Property(j => j.SAPCode).IsModified = true; entry.Property(j => j.InitiatedOn).IsModified = true; entry.Property(j => j.EndDate).IsModified = true; entry.Property(j => j.ProjectId).IsModified = true; // 并发令牌必须保持修改标记,用于EF并发校验 entry.Property(j => j.TimeStamp).IsModified = true; // 禁止修改创建信息 entry.Property(e => e.CreatedBy).IsModified = false; entry.Property(e => e.CreatedOn).IsModified = false; } }
内容的提问来源于stack exchange,提问作者user19639716
相关产品推荐
相关产品推荐

