C#代码重构:将内部foreach循环权限校验移至外部循环
重构嵌套循环,减少数据库权限校验调用次数
嘿,这个优化方向太对了——数据库调用的IO开销本来就大,内层循环反复调用完全是浪费资源。咱们只需要把权限校验提前到外层,每个Request只做一次校验,就能把性能提上去。
先看看你原来的代码大概是这样的(还原嵌套循环的问题场景):
foreach (var request in requestsList) { foreach (var item in request.anotherList) { // 每次内层循环都触发数据库调用,成本极高 if (myVar.hasPermissions(request)) { DoSomething(item); } } }
下面给你几种重构方案,按需选择:
方案1:先筛选有权限的Request,再处理内层集合
这种方式最直接,先把所有有权限的Request挑出来,再遍历它们的内层列表,完全避免重复校验:
// 第一步:外层循环一次性校验每个Request的权限 var authorizedRequests = new List<Request>(); foreach (var request in requestsList) { // 每个Request仅调用一次数据库 if (myVar.hasPermissions(request)) { authorizedRequests.Add(request); } } // 第二步:只处理有权限的Request的内层数据 foreach (var request in authorizedRequests) { foreach (var item in request.anotherList) { DoSomething(item); } }
如果习惯用LINQ,也可以简化成一行筛选(同步场景下):
var authorizedRequests = requestsList.Where(r => myVar.hasPermissions(r)).ToList(); foreach (var request in authorizedRequests) { foreach (var item in request.anotherList) { DoSomething(item); } }
方案2:缓存权限结果(需要保留无权限Request的处理逻辑)
如果你的业务需要对无权限的Request做额外处理(比如打日志、返回提示),可以先把每个Request的权限结果缓存到字典里,后续直接读取缓存:
// 先缓存所有Request的权限结果,仅一次数据库调用/每个Request var permissionCache = new Dictionary<int, bool>(); // 用Request的Id当键更稳妥,避免对象哈希问题 foreach (var request in requestsList) { permissionCache[request.Id] = myVar.hasPermissions(request); } // 遍历处理所有Request,直接用缓存的权限结果 foreach (var request in requestsList) { if (permissionCache.TryGetValue(request.Id, out bool hasPermission) && hasPermission) { foreach (var item in request.anotherList) { DoSomething(item); } } else { // 可选:处理无权限的情况 LogPermissionDenied(request); } }
进阶优化:批量查询权限
如果你的权限校验方法支持批量查询(比如传入一批Request Id,一次数据库调用返回所有权限结果),这会是性能最优的方案——毕竟多次单条查询不如一次批量查询高效:
// 收集所有Request的唯一标识 var requestIds = requestsList.Select(r => r.Id).ToList(); // 一次数据库调用,获取所有权限结果 var batchPermissionMap = myVar.GetBatchPermissions(requestIds); // 假设返回Dictionary<int, bool> // 遍历处理,直接用批量查询的结果 foreach (var request in requestsList) { if (batchPermissionMap.TryGetValue(request.Id, out bool hasPerm) && hasPerm) { foreach (var item in request.anotherList) { DoSomething(item); } } }
注意事项
- 用字典缓存权限时,尽量用Request的唯一标识(比如Id)作为键,而不是直接用Request对象——避免因为对象哈希值变化或者未正确实现
GetHashCode/Equals导致的缓存失效。 - 如果权限校验是异步方法(比如
HasPermissionsAsync),记得用await处理,LINQ筛选时要改成await Task.WhenAll或者异步循环,不要阻塞线程。
内容的提问来源于stack exchange,提问作者user1982519




