我有这样的情况:
与 EF Core 结合使用的 SQL Server 数据库(代码优先策略)。
我有一个包含名为
Inactive
的布尔标志的实体 - 含义:当为 TRUE 时,此条目被视为“不活动”。
示例代码:
var first_result = _dbSet.Where(b => !b.Inactive && some_other_check && some_more);
var first_item = first_result[0];
first_item.Inactive = true;
_dbSet.Update(first_item);
// identical with the first where - see above
var second_result = _dbSet.Where(b => !b.Inactive && some_other_check && some_more);
人们会期望这样的比较:
first_result.length() != second_result.length()
应该是真的,但事实并非如此!
更令人不安的是,第二个
WHERE
返回一个实际上拥有 first 项目和 Inactive = true
的集合,这应该是不可能的,因为 WHERE
条件 !Inactive
。
我花了一些时间仔细研究这个问题并得出结论,一定有某种缓存(?)并且我再次成为我的第一个结果,第一个项目设置为 Inactive=true...
怎么会这样?有人可以解释一下这种意外且(从我的角度来看)不合逻辑的行为吗?
提前非常感谢您!
PS:我发现(经过反复试验),在中间调用所涉及的
SaveChangesAsync
的 DbContext
方法将使其按预期工作
正如评论中提到的,您尚未对第一项的数据库提交任何更改。
当您迭代第二个查询时:
var second_result = _dbSet.Where(b => !b.Inactive && some_other_check && some_more);
EF 仍然构建一个查询表达式来针对数据库执行,并且您修改的第一个项目尚未更改,因此它将返回为“活动”。尚未考虑您已更新 EF 中的跟踪实例这一事实。如果您跟踪了要在最终结果中考虑的更改,但无法保存这些更改,那么您可以根据返回的物化集重新查询条件。例如:
var second_result = _dbSet.Where(b => !b.Inactive && some_other_check && some_more);
first_result.length() != second_result.ToList().Where(b => !b.Inactive).length()
这将反映对跟踪实体的更改,因为来自 secondary_result 的物化集中修改行的实例将是相同的引用。 但是,如果 secondary_result 使用
.AsNoTracking()
执行非跟踪查询,则此方法将不起作用,在这种情况下,您修改但未保存的行将被返回,并且将 not 指向相同的引用。
基本上,如果您对想要在未来查询中考虑的实体进行更改,请使用
SaveChanges
提交更改。