过滤IQueryable <t>会返回错误的结果</t>

时间:2011-11-30 17:31:54

标签: c# .net nhibernate linq-to-nhibernate

我遇到了一个非常令人惊讶的问题。

案例很简单:返回当前处于活动状态的所有实体,这意味着:根据布尔活动属性

过滤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

2 个答案:

答案 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