Linq如何基于用户角色为IQueryable动态添加多个筛选条件
解决方案
问题根因
你的原有代码存在两个问题:
- 每次赋值
query时都是基于原始的productionUnits数据源做筛选,直接覆盖了上一次的筛选结果 - 即使改为
query = query.Where(xxx)的写法,多个Where拼接的是AND逻辑,要求同时满足所有条件,自然无法匹配多个生产单元的需求
方案1:收集权限ID用Contains查询(最简单推荐)
如果各角色的筛选规则都是匹配生产单元ID,直接收集所有允许访问的ID,用Contains生成SQL的IN语句即可,全程在IQueryable层面执行,不会提前加载数据:
public void OnGet() { var productionUnits = _context.ProductionUnits; // 收集所有有权限的生产单元ID var allowedUnitIds = new List<int>(); if (User.IsInRole(CustomRole.AdministratorUAP1)) { allowedUnitIds.Add((int)ProductionUnitEnum.UAP1); } if (User.IsInRole(CustomRole.AdministratorUAP2)) { allowedUnitIds.Add((int)ProductionUnitEnum.UAP2); } // 其他角色规则同理添加 // 生成查询,EF会自动将Contains翻译为SQL IN语句 var query = productionUnits.Where(c => allowedUnitIds.Contains(c.Id)); // 后续正常使用query即可 }
如果用户无任何权限,allowedUnitIds为空,查询会直接返回空结果,符合预期。
方案2:动态拼接表达式树(适配复杂筛选规则)
如果不同角色的筛选规则不是简单的ID匹配(比如还要加状态、所属区域等其他条件),可以用OR逻辑拼接表达式树实现,这里可以用LINQKit提供的PredicateBuilder简化代码:
// 需先安装NuGet包LINQKit public void OnGet() { var productionUnits = _context.ProductionUnits; // 初始化条件构造器 var predicate = PredicateBuilder.New<ProductionUnit>(); if (User.IsInRole(CustomRole.AdministratorUAP1)) { // 可以写任意复杂的筛选条件,用Or拼接 predicate = predicate.Or(c => c.Id == (int)ProductionUnitEnum.UAP1 && c.IsActive); } if (User.IsInRole(CustomRole.AdministratorUAP2)) { predicate = predicate.Or(c => c.Id == (int)ProductionUnitEnum.UAP2); } // 其他角色规则同理添加 // 应用条件生成查询,EF Core可直接使用,旧版EF需要加.AsExpandable() var query = productionUnits.Where(predicate); }
内容的提问来源于stack exchange,提问作者Henrique Pombo
相关产品推荐
相关产品推荐

