c# - 具有多个连接和包含的 LINQ 查询不起作用
问题描述
我有一个运行正常的现有 LINQ 查询:
public partial class UserFacilityAccess
{
public int UserFacilityAccessId {get; set;}
public int FacilityId {get; set;}
public Guid AppUserId {get; set;}
}
appUsers = (from a in _context.AppUsers
join uf in _context.UserFacilityAccesses on new { p1 = a.AppUserId, p2 = FacilityID } equals new { p1 = uf.AppUserId, p2 = uf.FacilityId }
into jn
from c in jn.DefaultIfEmpty()
where c.UserFacilityAccessId == null
select a)
.OrderBy(o => o.AppUserDisplayName).ToList();
现在我有一个额外的要求,要检查一个 FacilityIds 列表。我所做的如下:
List<int> facilities = (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).ToList();
该列表成功填充,包含所有有效的设施 ID。我尝试对原始查询进行以下修改,现在无论设置如何,它都不会返回任何行,所以很明显我做错了。我可以将创建的设施加入到 UserFacilityAccesses 表中,但我不知道该怎么做。我想将 UserFacilityAccesses 限制为我创建的设施列表中具有 FacilityId 的记录。这是我尝试的:
appUsers = (from a in _context.AppUsers
join uf in _context.UserFacilityAccesses on new { p1 = a.AppUserId, p2 = FacilityID } equals new { p1 = uf.AppUserId, p2 = uf.FacilityId }
into jn
from c in jn.DefaultIfEmpty()
where c.UserFacilityAccessId == null && facilities.Contains(c.FacilityId)
select a)
.OrderBy(o => o.AppUserDisplayName).ToList();
这运行没有错误,但不返回任何行,因此显然 Contains 子句无法正常工作。我很困惑如何让它发挥作用。作为一项实验,我尝试更改 contains 子句,改为硬编码一个我知道将由 UserFacilityAccesses 表返回的 ID,并且只添加了 c.FacilityId == 5 并且也没有返回任何内容。提前致谢。
解决方案
您的两个查询都使用反连接(左外连接不包括与连接条件匹配的记录),但前者在连接前正确应用右侧过滤器,而后者在连接后尝试应用它,其中为时已晚,因为它已经被过滤以包含到记录中。
这就是我的意思。在第一次查询中
join uf in _context.UserFacilityAccesses
on new { p1 = a.AppUserId, p2 = FacilityID }
equals new { p1 = uf.AppUserId, p2 = uf.FacilityId }
into jn from c in jn.DefaultIfEmpty()
FacilityId
条件隐藏在连接条件(p2
键的)内。以上是等价于
(1)
join uf in _context.UserFacilityAccesses
.Where(x => FacilityId == x.FacilityID) // <-- pre filter
on a.AppUserId equals uf.AppUserId // <-- inner join
into jn from c in jn.DefaultIfEmpty() // <-- convert the inner join to left outer join
甚至更好理解的子查询语法
(2)
from c in _context.UserFacilityAccesses
.Where(x => FacilityId == x.FacilityID
&& a.AppUserId == x.AppUserId)
.DefaultIfEmpty()
where c == null
甚至更好(不要与反连接和什么是空的以及在哪里应用过滤器混淆),“没有匹配记录”的正常条件
(3)
where !_context.UserFacilityAccesses
.Any(x => FacilityId == x.FacilityID
&& a.AppUserId == x.AppUserId)
最好是使用导航属性(_context
变量表示您使用的是 EF6 或 EF Core)
(4)
where !a.UserFacilityAccesses
.Any(x => FacilityId == x.FacilityID)
所有这些(包括原件)都是等效的,仅在可读性上有所不同。但是最后 4 种语法允许您轻松更改FacilityId
原始语法无法实现的条件,并导致您出现不正确的位置/错误。
话虽如此,有问题的问题的解决方案是选择这 4 个查询中的任何一个并简单地替换
FacilityId == x.FacilityID
和
facilities.Contains(x.FacilityID)
推荐阅读
- c# - 开发了使用 gecko 的简单 winform 应用程序,不会在另一台计算机上运行
- php - getLastId 不会将最后一个 id 带入 foreach
- c - C 结构解释 push_vlan = (struct vlan_header *)((uint8_t *)new_eth + eth_size);
- css - 如果我创建两个具有相同名称但属性不同的类或 id 会发生什么,哪个将执行,为什么?
- php - SimpleXML 返回组合的 XML 内容
- reactjs - CSS中的Flex定位
- python - 使用 if 语句将一行的整个列与另一个数据框进行比较。在蟒蛇
- c# - 在编排中接收不同类型的 XML 请求
- vue.js - Vue组件正在渲染但页面为空白。我可以通过检查页面来查看代码
- java - Spring Webflux webclient 出现问题,尝试发送发布请求时没有任何反应