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

mik*_*kus 2 .net c# nhibernate linq-to-nhibernate

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

情况很简单:返回当前活动的所有实体,这意味着:GetAll()根据布尔活动属性过滤方法返回的所有结果

public IQueryable<T> GetAllActive()
{
      return implementation.GetAll().Where(a => ((IDeactivable)a).Active);  
}
Run Code Online (Sandbox Code Playgroud)

其中GetAll()实现对象的方法定义为:

public IQueryable<T> GetAll();
Run Code Online (Sandbox Code Playgroud)

问题是,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);
}
Run Code Online (Sandbox Code Playgroud)

结果是:

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
Run Code Online (Sandbox Code Playgroud)

Rob*_*vey 5

返回时IQueryable<T>,实际上并没有返回结果集.你要返回的是一个可以查询的对象.

延迟执行该.Where()方法,直到您(或调用您的方法的人)实际上强制执行Linq链.这使得下游客户端可以将其他Linq方法应用于结果,并且仍然可以对整个Linq链进行延迟评估.

因此,当您说IQueryable<T>正在返回所有记录时,您可能正在查看调试器中的结果,并且它会向您显示没有过滤的原始数据集(因为.Where()尚未执行).

转换为IEnumerable工作的原因是因为它触发Linq命令链的执行,结果是一个真实的列表,而不是可以查询的对象. 调用ToList()ToArray()也将触发执行.

简而言之,在测试过程中,您可以确定从Linq方法看到正确结果的唯一方法是强制执行Linq链:

foreach(var record in GetAllActive.ToList())
{
    // Display each record
}
Run Code Online (Sandbox Code Playgroud)

有关其工作原理的一点说明,请参阅使用延迟执行.它包含一个示例,说明如何IQueryableusing块中返回一个实际的麻烦,因为该IQueryable对象在查询执行之前被释放.