具有字段值的 EF Core 结果存在于数组中
Posted
技术标签:
【中文标题】具有字段值的 EF Core 结果存在于数组中【英文标题】:EF Core results with a field value exists in array 【发布时间】:2021-11-02 09:47:48 【问题描述】:我想获取用户 StatusId
存在于包含我需要的状态 ID 的数组中的用户数据。
我使用以下代码,但结果是数组中的最后一个值。
int[] statusIds = 1,4,8
IQueryable<User> result = _database.User;
foreach (var item in statusIds)
result = result.Where(x => x.StatusId == item);
此代码仅获取 StatusId
= 8 的用户。
【问题讨论】:
你试过result.Where(x => statusIds.contains( x.StatusId));
【参考方案1】:
在 Where 的结果上连续使用 Where 的问题,就像你在这里做的那样:
foreach (var item in statusIds)
result = result.Where(x => x.StatusId == item);
当你在前面的 Where 之上复合 Where 时,它的作用是 AND,而不是 OR,所以你的查询最终相当于:
_database.User.Where(u => u.StatusId == 1 && u.StatusId == 4 && u.StatusId == 8)
这自然不会产生任何结果,因为没有记录可以同时是所有 3 种状态
旁注:您报告输出只是数组中的最后一个元素,我认为这是与延迟执行和捕获 foreach 循环变量值有关的单独问题(尽管我不相信它会在现代C#)。到执行查询时(循环完成后),循环变量已经使用了 3 次,但查询中使用的值不是变量在每次循环迭代期间的 3 个不同值,而是单个循环的 3 次重复循环结束时它的值。因此,有效地,查询运行是
_database.User.Where(u => u.StatusId == z && u.StatusId == z && u.StatusId == z)
而z
的值为 8。从逻辑上讲,这可以工作,因为只测试了一个值。你可以阅读更多关于这个特殊的奇怪之处 here
即使您组合了一个结构来处理“使用变异捕获的循环变量进行延迟执行”问题,您仍然会遇到“statusid = 1 和 4 和 8 肯定是假”的问题,所以您需要另一种方法
类似这样的东西将被映射为x IN (collection)
:
_database.User.Where(u => statusIds.Contains(u.StatusId))
_database.User.Where(u => statusIds.Any(sid => sid == u.StatusId))
【讨论】:
是的,你是对的,我当时没有任何状态 id 为 8 的记录,所以我认为输出是状态 id 为 8 的记录。【参考方案2】:问题的解决方案由@Greg 在问题的评论中给出。
我只是想正式布置一下答案。
方法一
int[] statusIds = 1,4,8
IQueryable<User> result = _database.User.Where(x => statusIds.Contains( x.StatusId));
方法2
int[] statusIds = 1,4,8
IQueryable<User> result = _database.User.Where(x => statusIds.Any(n => n == x.StatusId));
这两种方法的区别在于.Contains
函数在你需要类似“...WHERE x IN (1,2,3)”结果的SQL查询时使用,这基本上是一个标准的比较情况。
而.Any
允许您为多值比较或唯一条件提供自己的谓词。请记住,如果您有大型数据集,谓词将在本地执行,这是一个很大的禁忌。
一般来说,尽量避免.Any
。经验法则是将处理卸载到数据库,而不是在本地导入所有记录和过滤。
调试 SQL 查询
要查看 EFCore 生成的实际 SQL 查询(我强烈推荐),请阅读 Christian Findlay 的 blog post。
【讨论】:
以上是关于具有字段值的 EF Core 结果存在于数组中的主要内容,如果未能解决你的问题,请参考以下文章
EF Core 中DbContext不会跟踪聚合方法和Join方法返回的结果