带多Join的LINQ查询新增Contains过滤条件后无返回结果求助
问题根源
你修改后的代码存在逻辑错误:原查询用DefaultIfEmpty()实现左外连接,当用户没有匹配的UserFacilityAccess记录时,变量c为null。此时你在where中同时判断c.UserFacilityAccessId == null和facilities.Contains(c.FacilityId),null不可能存在于int类型的facilities列表中,所以整个where条件永远不成立,自然没有返回结果。
解决方案
你需要将设施ID的过滤逻辑提前到左连接的右表过滤阶段,只对目标设施范围内的权限记录做关联查询,正确代码如下:
appUsers = (from a in _context.AppUsers // 提前过滤UserFacilityAccesses,仅保留目标设施范围内的记录 join uf in _context.UserFacilityAccesses.Where(x => facilities.Contains(x.FacilityId)) on new { p1 = a.AppUserId, p2 = FacilityID } equals new { p1 = uf.AppUserId, p2 = uf.FacilityId } into jn from c in jn.DefaultIfEmpty() // c为null即代表用户在目标设施范围内没有关联权限 where c.UserFacilityAccessId == null select a) .OrderBy(o => o.AppUserDisplayName) .ToList();
也可以用更简洁的Any写法实现相同逻辑,可读性更高:
appUsers = _context.AppUsers .Where(a => !_context.UserFacilityAccesses .Any(uf => uf.AppUserId == a.AppUserId && uf.FacilityId == FacilityID && facilities.Contains(uf.FacilityId))) .OrderBy(o => o.AppUserDisplayName) .ToList();
可选性能优化
如果facilities列表数据量较大,可以不用提前调用ToList()转为内存集合,直接保留为IQueryable类型,让EF将整个逻辑翻译为SQL一次性执行,减少内存占用提升性能:
// 不调用ToList,保留为IQueryable<int>类型的子查询 var facilitiesQuery = from f in _context.Facilities join c in _context.Clients on f.ClientID equals c.ID join g in _context.Groups on c.GroupID equals g.ID where ((GroupID == 0 || g.ID == GroupID) && (ClientID == 0 || c.ID == ClientID)) select f.ID; // 主查询直接关联子查询 appUsers = _context.AppUsers .Where(a => !_context.UserFacilityAccesses .Any(uf => uf.AppUserId == a.AppUserId && uf.FacilityId == FacilityID && facilitiesQuery.Contains(uf.FacilityId))) .OrderBy(o => o.AppUserDisplayName) .ToList();
内容的提问来源于stack exchange,提问作者nonesuch
相关产品推荐
相关产品推荐

