2016-05-06 130 views
2

考虑以下功能:为什么Queryable.SelectMany(...)重载接受Func <S,IEnumerable <R>>而不是Func <S,IQueryable <R>>?

IQueryable<Bar> foo(IEnumerable<IQueryable<Bar>> sources) 
{ 
    return 
     from source in sources.AsQueryable() 
     from bar in source 
     where bar.Xzy == 123 
     select bar; 
} 

直觉上,我希望它可以在每个源的上下文中执行的“从...这里......选择”表达。不过,我相信它只会针对源执行“from ... in ...”部分,而不是执行“where ... select”部分作为LINQ-to-Objects查询。最终的结果是SomeTable的所有行将从每个源检索,而不是只匹配“where”条件的那些。

乍一看,我的猜测是,这是因为对SelectMany的调用导致“源”表达式被隐式转换为IEnumerable <Bar>。我不确定实现是什么样子,但是接受Func < S,IQueryable <R> > >而不是合理的,这样where ... select表达式就传递给IQueryable提供者了吗?

+0

“source.SomeTable”的类型是什么?如果将'foo'的返回类型更改为'IQueryable ',会发生什么? – Lee

+0

@Lee,对不起,我认为SomeTable属性在示例中没有意义。我将“source.SomeTable”改为“source”。我认为将返回类型更改为IQueryable 不会更改查询的行为。 – Aaron

+0

返回类型应做出不同,因为'IEnumerable '重载返回'IEnumerable ',而'IQueryable'重载返回'IQueryable '。因此,如果返回类型更改为“IQueryable ”,并且选择了“SelectMany”的IEnumerable超载,那么您的函数将无法编译。 – Lee

回答

1

我认为这归结于编译器如何解释您的查询。什么它实际上是将你的查询转换是这样的(严格的左到右解析):

sources.AsQueryable() 
     .SelectMany(source => source) 
     .Where(bar => bar.Xzy == 123) 
     .Select(bar => bar) 

重要的是,你的过滤器上的第一枚举每个源的结果进行操作。

还要注意的是,实际上AsQueryable()是多余的,因为它不是让你的来源还有任何可查询比他们已经是,它使得枚举你的源代码可查询,并且你没有查询该设置反正,你正在查询个人来源。

你真正想要的是更喜欢这个,我觉得:

sources.SelectMany(source => source.Where(bar => bar.Xzy == 123)) 

这改变了相对的“订单的优先级”条款的解释。

我实际上不知道如何使用LINQ语法来创建后者。

UPDATE:其实,这里有一个方法:

from source in sources 
let qsource = (from bar in source 
       where bar.Xzy == 123 
       select bar) 
from result in qsource 
select result 

我一般倾向于更喜欢使用手艺由于这个原因扩展方法不平凡的查询。

+0

谢谢,我使用LINQ表达式就像在更新的答案中一样。只是试图绕过我的头,为什么原来的查询没有做到我所期望的。我最初在我的“真实”查询中添加了AsQueryable()调用,它认为它通过强制使用Queryable.SelectMany而不是Enumerable.SelectMany来解决我的问题(它没有)。 – Aaron

相关问题