在 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),则仅包含基于预定boolallowed 的行。

尝试:

我以为我可以这样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的主要内容,如果未能解决你的问题,请参考以下文章

C# LINQ 与两个不同数据集上的条件 where 子句连接

关于为啥这个 linq ef 核心查询不会执行的建议

有条件地将 where 子句添加到 eloquent 查询

LINQ to SQL Where 子句可选条件

如何有条件地应用 Linq 运算符?

请教linq中orderby子句与where子句共存时排序失效问题