我遇到了一个非常令人惊讶的问题。
案例很简单:返回当前处于活动状态的所有实体,这意味着:根据布尔活动属性
过滤GetAll()
方法返回的所有结果
public IQueryable<T> GetAllActive()
{
return implementation.GetAll().Where(a => ((IDeactivable)a).Active);
}
其中GetAll()
实现对象的方法定义为:
public IQueryable<T> GetAll();
问题是,GetAllActive()
返回所有记录,无论其Active属性的值如何,就像没有Where子句一样。
它可能是什么原因?
注意:简化了代码,检查T
类型以实现IDeactivable
接口。在运行时也不会抛出异常。
编辑:实现对象返回的IQueryable来自NHibernate
Edit2:我使用以下代码检查实体的实际值(除了使用VS Debugger):
foreach (var a in active) { //active -> filtered IQueryable before return
_logger.Warn(a.Id);
_logger.Warn(((IDeactivable)a).Active);
}
结果是:
11/30/2011 18:10:00 WARN xxx.Repository`1.GetAllActive: 70db43fa-2361-4c1f-a8e5-9fab012b5a2b
11/30/2011 18:10:01 WARN xxx.Repository`1.GetAllActive: False
11/30/2011 18:10:02 WARN xxx.Repository`1.GetAllActive: 5493c9bb-ec6e-4690-b5d6-9fab012b5b16
11/30/2011 18:10:02 WARN xxx.Repository`1.GetAllActive: True
答案 0 :(得分:5)
当您返回IQueryable<T>
时,您实际上并未返回结果集。你要返回的是一个可以查询的对象。
执行.Where()
方法 deferred ,直到您(或某人调用您的方法)实际上强制执行Linq链。这使得下游客户端可以将其他Linq方法应用于结果,并且仍然可以对整个Linq链进行延迟评估。
因此,当您说IQueryable<T>
正在返回所有记录时,您可能正在查看调试器中的结果,它会向您显示没有过滤的原始数据集(因为.Where()
尚未执行)。
转换为IEnumerable
的原因是因为它触发了Linq命令链的执行,并且结果是一个真实的列表,而不是一个可以查询的对象。调用ToList()
或ToArray()
也会触发执行。
简而言之,您可以确保在测试过程中看到Linq方法的正确结果的唯一方法是强制执行Linq链:
foreach(var record in GetAllActive.ToList())
{
// Display each record
}
有关其工作原理的一点说明,请参阅Working with Deferred Execution。它包含一个示例,说明如何从IQueryable
块返回using
时遇到麻烦,因为IQueryable
对象在查询执行之前会被处理。
答案 1 :(得分:0)
我尝试了几种不同的方法,最后我发现我的部分代码还没有测试过。事实证明,使用Where子句时LINQ查询到NHibernate会导致一些问题,我之前没有注意到。
最终,我发现,我使用的是错误版本的LINQ to NHibernate QueryProvider(不是NH 3.0中包含的版本),这是一个已知问题。现在我已经摆脱它,一切正常。谢谢你的帮助,GUYS!你指出了我正确的方向。
以下主题中描述了提到的问题: Problem with linq query