在 LINQ WHERE 子句中有条件地允许 null
Posted
技术标签:
【中文标题】在 LINQ WHERE 子句中有条件地允许 null【英文标题】:Conditionally allowing for null in LINQ WHERE clause 【发布时间】:2013-12-09 03:04:24 【问题描述】:工作:
我正在尝试在我的 Contacts
表和我的 Permissions
表之间执行 LEFT OUTER JOIN。我有这个正常工作的基础,无论他们是否有相应的权限,都可以取回一个联系人列表。
// Query for Contacts
from contact in Contacts
join permission in Permissions on contact.Id equals permission.ObjectId into permissionGrp
from p in permissionGrp.DefaultIfEmpty()
where p==null || (p!=null && /* ... condition based on the existence of a permission */)
select new contact, permission = p ;
生成的 WHERE SQL:
WHERE
(t1.PermissionId IS NULL OR
((t1.PermissionId IS NOT NULL AND ... other conditions ... )))
问题:
我想修改上述内容以引入“后备”检查;没有按预期工作。
要求:
当没有Contact
对应的Permission
时(即p==null
),则仅包含基于预定bool
值allowed
的行。
尝试:
我以为我可以这样where (p==null && allowed) || ...
:
// Fallback permission
bool allowed = false;
// Query for Contacts
from contact in Contacts
join permission in Permissions on contact.Id equals permission.ObjectId into permissionGrp
from p in permissionGrp.DefaultIfEmpty()
/* Added bool condition 'allowed' to control inclusion when 'p' is null */
where (p==null && allowed) || (p!=null && /* ... condition based on the existence of a permission */)
select new contact, permission = p ;
预期:
当allowed = false
(不接受null
权限)
WHERE
((t1.PermissionId IS NOT NULL AND ... other conditions ... ))
当allowed = true
(接受null
权限)
WHERE
(t1.PermissionId IS NULL OR
((t1.PermissionId IS NOT NULL AND ... other conditions ... )))
实际结果:
即使true
也总是像allowed=false
一样输出?
WHERE
((t1.PermissionId IS NOT NULL AND ... other conditions ... ))
总结:
我希望我只是在做一些很容易解决的傻事。
如何根据给定的bool
值过滤我的null
值记录?
【问题讨论】:
您是通过运行实际检查查询结果还是只是尝试查看已翻译的 SQL 查询?看起来allowed
总是被翻译成false
。
我在 LinqPad 中运行了查询,它显示了 SQL 输出。它没有返回预期的结果集。
我没有太多使用 LINQPad,只是使用了它的一些功能,但我认为您应该在 Visual Studio 中尝试您的代码。
【参考方案1】:
你在这里执行GroupJoin
。所以第一部分的结果,permissionGrp
,是一个匿名类型IGrouping<Permission>
。这已经相当于 OUTER JOIN。
您可以通过有条件地测试IGrouping<Permission>
是否包含元素来实现您想要的:
from contact in Contacts
join permission in Permissions on contact.Id equals permission.ObjectId
into permissionGrp
where allowed || g.Any()
from p in permissionGrp.DefaultIfEmpty()
select new contact, permission = p ;
注意from p in permissionGrp
再次展平了分组,因此.DefaultIfEmpty()
对于allowed == true
的情况仍然是必需的。
【讨论】:
嗨格特,感谢您的回答。我可以看到你在做什么。不幸的是,我收到了一个例外Argument Exception: Invalid type owner for DynamicMethod
。我认为这可能是我的 ORM LightSpeed 的一个缺点。我将尝试一些更简单的测试来确定这是否是 ORM 错误,我怀疑它是。【参考方案2】:
我怀疑我正在使用的 ORM (LightSpeed) 中存在错误,我会提请他们注意。
解决方法
我找到了一个合适的解决方法,使用let
子句。
// Fallback permission
bool allowed = false;
// Query for Contacts
from contact in Contacts
join permission in Permissions on contact.Id equals permission.ObjectId into permissionGrp
from p in permissionGrp.DefaultIfEmpty()
/* Work around for 'allowed' not being honoured properly, using 'let' */
let isAllowed = allowed
/* Added bool condition 'isAllowed' to control inclusion when 'p' is null */
where (p==null && isAllowed) || (p!=null && /* ... condition based on the existence of a permission */)
select new contact, permission = p ;
结果
它现在使用已知值与自身的比较作为布尔检查。在这种情况下t0.ContactId
。
SQL 当allowed=true
... t0.ContactId = t0.ContactId
WHERE
((t1.PermissionId IS NULL AND t0.ContactId = t0.ContactId) OR
(t1.PermissionId IS NOT NULL AND ... other conditions ...))
SQL 当allowed=false
... t0.ContactId <> t0.ContactId
WHERE
((t1.PermissionId IS NULL AND t0.ContactId <> t0.ContactId) OR
(t1.PermissionId IS NOT NULL AND ... other conditions ...))
【讨论】:
以上是关于在 LINQ WHERE 子句中有条件地允许 null的主要内容,如果未能解决你的问题,请参考以下文章