Linq To Entities多键值对Where条件查询优化方案咨询
问题与优化方案
表结构与数据
Tracking 表
Id DateTime Description 100 2022-12-08T10:53 Package Picked Up 101 2022-12-08T10:59 Package is in lorry
TrackingDetails 表(TrackingId 为 Tracking 表 Id 的外键)
Id TrackingId Key Value 45 100 Location Davis place 46 100 ResponsiblePerson John 47 100 ReferenceNo 102A788 48 100 Status PickedUp 49 101 Location Torington 50 101 ResponsiblePerson Driver : Mick 51 101 ReferenceNo 102A788 52 101 Status InTransit
查询需求
判断是否存在同时满足以下两个条件的关联记录:
- TrackingDetails 中存在
Key='ReferenceNo'且Value='102A788' - TrackingDetails 中存在
Key='Status'且Value='PickedUp'
原有查询的问题
Method 1 错误原因
var isExist = _TrackingRepository.GetAll().Include(x => x.Details) .Where(x => x.Details.Where(y => (y.Key == "ReferenceNo" && y.Value == "102A788") && (y.Key == "Status" && y.Value == "PickedUp")).Any()) .Any();
单个 TrackingDetails 记录的 Key 不可能同时等于 ReferenceNo 和 Status,内层 Where 永远返回空集合,导致最终结果为 false。
Method 2 问题
var isExist = _TrackingRepository.GetAll().Include(x => x.Details) .Where(x => x.Details.Where(y => y.Key == "ReferenceNo").Any()) .Include(x => x.Details).Where(x => x.Details.Where(y => y.Value == "102A788").Any()) .Any();
结果正确但写法冗余,重复调用 Include(EF 会自动去重,但代码可读性差),多条件时扩展性不足。
优化后的查询方案
方案1:双Any判断(推荐)
逻辑清晰,无需重复 Include,EF 会转换为高效的 SQL 关联查询:
var isExist = _TrackingRepository.GetAll() .Any(t => t.Details.Any(d => d.Key == "ReferenceNo" && d.Value == "102A788") && t.Details.Any(d => d.Key == "Status" && d.Value == "PickedUp"));
核心逻辑:检查是否存在某个 Tracking,其关联的 Details 中同时存在符合两个条件的记录。
方案2:动态多条件支持
适合需要新增多个匹配条件的场景,通过字典管理条件:
var requiredConditions = new Dictionary<string, string> { {"ReferenceNo", "102A788"}, {"Status", "PickedUp"} }; var isExist = _TrackingRepository.GetAll() .SelectMany(t => t.Details) .Where(d => requiredConditions.ContainsKey(d.Key) && requiredConditions[d.Key] == d.Value) .GroupBy(d => d.TrackingId) .Any(g => g.Select(d => d.Key).Distinct().Count() == requiredConditions.Count);
新增条件只需在字典中添加键值对即可。
方案3:直接查询子表(性能最优)
如果不需要 Tracking 实体的其他数据,直接操作 TrackingDetails 表减少关联开销:
var isExist = _TrackingDetailsRepository.GetAll() .GroupBy(d => d.TrackingId) .Any(g => g.Any(d => d.Key == "ReferenceNo" && d.Value == "102A788") && g.Any(d => d.Key == "Status" && d.Value == "PickedUp"));
内容的提问来源于stack exchange,提问作者weeraa
相关产品推荐
相关产品推荐

