You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在ABP Framework 5.3中如何用EF Core实现表并发检查及关联表保护?

针对ABP Framework 5.3 + .NET 6 + SQL Server场景的问题解答

问题1:如何确保更新Table-W-1时,关联的Table-R-1和Table-R-2记录不被修改?

可以从多个层面逐步强化控制,按严格程度排序:

  • 代码层基础控制

    • 为Table-R-1和Table-R-2实现只读仓储:自定义仓储时仅继承IReadOnlyRepository<T>而非IRepository<T>,只暴露GetAsync、GetListAsync等查询方法,屏蔽所有修改类接口(UpdateAsync、DeleteAsync等)。
    • 实体设为只读:将Table-R-1/Table-R-2实体的属性setter设为private,仅通过构造函数初始化数据,从根源上避免代码中修改实体字段。
    • 应用服务层限制:在处理Table-W-1更新的应用服务方法中,仅调用Table-R-1/Table-R-2的查询逻辑,绝不触发任何修改操作。
  • 数据库权限兜底
    给应用使用的SQL Server数据库账号,仅赋予Table-R-1和Table-R-2的SELECT权限,移除INSERT、UPDATE、DELETE权限。即便代码出现疏漏,数据库也会直接拒绝修改请求。

  • ABP拦截器强制拦截
    实现ABP方法拦截器,拦截所有针对Table-R-1/Table-R-2实体的修改方法,一旦检测到调用直接抛出业务异常:

    public class ReadOnlyEntityInterceptor : MethodInterceptorBase
    {
        protected override void Intercept(IAbpMethodInvocation invocation)
        {
            var targetEntityType = invocation.Method.GetParameters()
                .FirstOrDefault(p => typeof(IEntity).IsAssignableFrom(p.ParameterType))?.ParameterType;
            
            if (targetEntityType == typeof(TableR1) || targetEntityType == typeof(TableR2))
            {
                throw new BusinessException("Table-R-1和Table-R-2为只读表,禁止修改");
            }
            
            base.Intercept(invocation);
        }
    }
    

    在模块类中注册该拦截器,对仓储的修改方法生效即可。


问题2:同一作用域内读取Table-R-1/Table-R-2后更新Table-W-1,能否在前者被修改时触发并发异常阻止写入?

默认情况下不会触发并发异常,因为EF Core(ABP默认ORM)搭配SQL Server默认的「读已提交」隔离级别时,事务内读取的是数据快照,其他事务对Table-R-1/Table-R-2的修改不会被当前事务感知,当前事务仍会正常提交Table-W-1的更新。

要实现「前者被修改则阻止写入」的需求,可通过两种方案实现:

方案1:乐观并发控制(推荐)

  1. 给Table-R-1和Table-R-2添加RowVersion列(SQL Server用timestamp类型,EF Core标记[Timestamp]属性):
    public class TableR1 : Entity<Guid>
    {
        // 其他业务属性
        
        [Timestamp]
        public byte[] RowVersion { get; set; }
    }
    
  2. 在处理Table-W-1更新的事务流程中:
    • 读取Table-R-1/Table-R-2时,记录对应的RowVersion值;
    • 提交Table-W-1更新前,重新查询目标记录的最新RowVersion并与之前的值对比;
    • 若不一致,说明记录已被其他事务修改,抛出并发异常并回滚当前事务。

方案2:悲观并发控制

  1. 设置事务隔离级别为可重复读:在ABP应用服务方法上标记[UnitOfWork(IsolationLevel = IsolationLevel.RepeatableRead)],此级别保证事务内多次读取同一数据的结果一致,其他事务无法修改当前事务已读取的记录(会被阻塞直到当前事务完成)。
  2. 添加查询锁定提示:读取Table-R-1/Table-R-2时,使用WITH (UPDLOCK, HOLDLOCK)强制对读取记录加更新锁,直到事务结束:
    var targetR1 = await _dbContext.TableR1
        .FromSqlRaw("SELECT * FROM [Table-R-1] WHERE Id = {0} WITH (UPDLOCK, HOLDLOCK)", targetId)
        .FirstOrDefaultAsync();
    
    这种方式下,其他事务尝试修改锁定记录会被阻塞;若极端场景下记录被修改,当前事务提交时会触发并发异常。

内容的提问来源于stack exchange,提问作者Anirban Paul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 12:48:15