如何在DbContextBulkExtensions.BulkUpdate中实现并发检查?
DbContextBulkExtensions.BulkUpdate并发校验实现方案
DbContextBulkExtensions默认不支持EF的ConcurrencyCheck特性,要在批量更新时实现并发冲突检测并抛出异常,可通过以下两种方式处理:
方式一:基于原始状态列的条件校验
在批量更新时,强制带上原始状态值作为更新条件,同时检查实际更新行数是否符合预期:
// 假设你要更新的用户集合是updatedUsers,每个用户的原始状态都是"new" var bulkConfig = new BulkConfig { // 指定要传入的原始列(包含状态和ID) ColumnInputExpression = u => new { u.Status, u.Id }, // 限定更新条件为ID和原始状态匹配 UpdateByProperties = new List<string> { nameof(User.Id), nameof(User.Status) } }; context.BulkUpdate(updatedUsers, bulkConfig); var affectedRows = context.SaveChanges(); // 对比预期更新行数和实际影响行数,判断是否有并发冲突 if (affectedRows != updatedUsers.Count) { throw new InvalidOperationException("并发更新冲突,部分记录已被修改"); }
这种方式会生成类似UPDATE Users SET Status = @newStatus WHERE Id = @id AND Status = @originalStatus的SQL,只有当数据库中记录的状态和你获取的原始状态一致时才会更新。如果并发场景下某条记录的状态已被修改,这条记录的更新会失效,通过对比影响行数就能检测到冲突。
方式二:用行版本列做并发校验(更可靠)
给User实体添加行版本列,利用数据库的行版本机制实现精准冲突检测:
// 实体定义 public class User { public int Id { get; set; } public string Status { get; set; } // 标记为时间戳列,数据库会自动维护其值 [Timestamp] public byte[] RowVersion { get; set; } } // 批量更新配置 var bulkConfig = new BulkConfig { // 开启并发异常抛出模式 ConcurrencyMode = ConcurrencyMode.ThrowException, // 指定行版本列作为并发校验依据 ConcurrencyCheckExpression = u => u.RowVersion }; try { context.BulkUpdate(updatedUsers, bulkConfig); } catch (ConcurrencyException ex) { // 捕获到并发冲突异常,这里做业务处理 throw; }
这种方式下,DbContextBulkExtensions会自动对比传入的RowVersion和数据库中的值,一旦不匹配就抛出ConcurrencyException,和EF原生Update的并发校验逻辑完全一致,而且能检测到任何行数据的变更,不止是状态列。
关键注意点
- 若用方式一,要确保获取原始状态后到批量更新前,没有其他操作修改状态,否则会误判。
- 不同数据库的行版本实现略有差异,比如SQL Server用
timestamp/rowversion类型,MySQL可以用BINARY(16)加触发器维护。 - 方式二是推荐方案,因为它的冲突检测更精准,无需手动维护原始状态值。
内容的提问来源于stack exchange,提问作者Napa
相关产品推荐
相关产品推荐

