Linq Projection 在 NHibernate 3.2 中被错误地缓存
Posted
技术标签:
【中文标题】Linq Projection 在 NHibernate 3.2 中被错误地缓存【英文标题】:Linq Projection being cached incorrectly in NHibernate 3.2 【发布时间】:2012-01-05 22:59:41 【问题描述】:给定一个像下面这样的简单投影,NHibernate 将缓存查询计划,当查询相同时不更新变量的值:
int argValue = 1;
var result1 = database.Users.Select(x => new x.Name, BadArg = argValue).First();
argValue = 2;
var result2 = database.Users.Select(x => new x.Name, BadArg = argValue).First();
预期
result1 值为 Name = "Bob" 和 BadArg = 1
result2 值为 Name = "Bob" 和 BadArg = 2
实际
result1 值为 Name = "Bob" 和 BadArg = 1
result2 值将是 Name = "Bob" 和 BadArg = 1
如果您没有预料到,这显然会导致很多疯狂的行为。我在 NHibernate 的错误跟踪中看到了与此类似的 couple bug reports,但自去年 5 月以来一直没有采取任何行动。所以要么没有人经常使用 Linq to Nhibernate,要么有一些我不知道的解决方法。
在我深入研究 NHibernate 源之前,有没有办法禁用查询计划缓存以防止这种行为或其他一些解决方法,或者是否有人应用了上述链接中的补丁?
注意
该示例旨在使问题保持简单,实际上我有一个复杂的投影,我想将其保留为 IQueryable,过早转换为 IEnumerable 是行不通的。
更新 不适用于 Nhibernate 3.2.1 的 github master
【问题讨论】:
出于好奇,我一直在研究这个问题,看起来问题不在于 NHibernate 的缓存,而是 Linq 表达式树缓存。问题是,表达式树被简化了,变量引用被简化为一个常量表达式,之后缓存键被构造——但到那时,范围变量 ref 和常量之间的区别就不清楚了。 :-/ 我也遇到过这个问题,太浪费时间了。每隔几个月它就会重新出现,我已经忘记了它,只是再次重新发现它。在我的例子中,我不是针对匿名对象进行投影,而是对我自己创造的对象进行评估。 【参考方案1】:如果您想完全避免缓存,请尝试将您的代码更改为:
int argValue = 1;
var result1 = database.Users.AsEnumerable().Select(x => new x.Name, BadArg = argValue).First();
argValue = 2;
var result2 = database.Users.AsEnumerable().Select(x => new x.Name, BadArg = argValue).First();
发生的情况是,您最终将使用 System.Linq Select 方法而不是使用 NHLinq Select 方法——有效地防止 NHibernate 缓存投影。当然,缺点是您将在内存中进行投影,因此您最终会从用户表中选择所有字段,而不仅仅是您想要的。
【讨论】:
我知道这会起作用,但有点违背了目的。对于这个问题,我的示例保持简单,但实际上我想将其保留为 IQueryable。【参考方案2】:以下是禁用一级缓存的方法: http://darioquintana.com.ar/blogging/2007/10/08/statelesssession-nhibernate-without-first-level-cache/
我不认为你可以禁用二级缓存,但我认为你不必为这个问题。
(基本上您需要为每个查询创建和销毁会话。)
如果您发现问题仍然存在,则必须按照 Rytmis 的建议进行操作。
【讨论】:
创建一个不同的会话只是为了执行一个查询似乎有点笨拙,并且可能会对来自另一个会话的对象产生不良后果。 取决于您要投影的内容——如果您的投影仅包含标量值,那应该不是问题。但你是对的,感觉不对。以上是关于Linq Projection 在 NHibernate 3.2 中被错误地缓存的主要内容,如果未能解决你的问题,请参考以下文章
NHibernate、Castle、Linq 之间的区别——它们针对谁?
Mongo $projection,??????????????
大数据ClickHouse进阶(二十一):ClickHouse的Projection投影