如何在LINQ多Where子句中复用查询值避免重复查找?
优化LINQ查询:复用重复计算的分类数据
哈哈,这个场景太常见了!你现在的写法确实会让每个Where子句都重复调用GetPropertyValue来提取分类数据,既冗余又影响性能,给你两种实用的优化方案:
方案一:通过Select投影临时对象缓存数据
我们可以先把每个Item和它对应的分类ID集合一次性投影出来,后续的筛选直接复用这个已经计算好的集合:
// 第一步:一次性提取所有需要的数据,缓存分类ID集合 var itemsWithCachedCategories = _umbracoHelper.GetPage(ItemsPage.ModelTypeAlias).Children .Select(item => new { OriginalItem = item, CategoryIds = item.GetPropertyValue<IEnumerable<IPublishedContent>>(UmbracoAlias.Item.Categories)? .Select(cat => cat.Id) ?? Enumerable.Empty<int>() }); // 第二步:用缓存好的分类ID做筛选,避免重复计算 var filteredItems = itemsWithCachedCategories .Where(x => level1Category == 0 || x.CategoryIds.Contains(level1Category)) .Where(x => !level2Categories.Any() || x.CategoryIds.Intersect(level2Categories).Any()) .Select(x => x.OriginalItem); // 最后还原回原Item对象
为什么这么做?
Select只执行一次,每个Item的分类ID集合只会被计算一次,后续的Where都是基于缓存的集合判断- 加入了
?? Enumerable.Empty<int>()的空值判断,避免当Item没有分类时,GetPropertyValue返回null导致后续调用Contains/Intersect抛出空引用异常
方案二:使用LINQ查询表达式的let关键字
如果你更习惯用LINQ查询表达式语法,let关键字可以帮我们在查询过程中临时存储计算结果,实现同样的缓存效果:
var filteredItems = from item in _umbracoHelper.GetPage(ItemsPage.ModelTypeAlias).Children let categoryIds = item.GetPropertyValue<IEnumerable<IPublishedContent>>(UmbracoAlias.Item.Categories)? .Select(cat => cat.Id) ?? Enumerable.Empty<int>() where level1Category == 0 || categoryIds.Contains(level1Category) where !level2Categories.Any() || categoryIds.Intersect(level2Categories).Any() select item;
这个方案的优势:
- 语法更紧凑,逻辑连贯,不需要额外的临时变量存储投影结果
let关键字会自动帮我们缓存categoryIds的计算结果,后续的where子句直接复用
两种方案核心思路一致:提前计算并缓存需要重复使用的昂贵数据,避免在多个筛选条件中重复执行属性获取和转换操作,既提升了性能,也让代码更易维护。
内容的提问来源于stack exchange,提问作者wingyip
相关产品推荐
相关产品推荐

