【问题标题】:Implicit AsQueryable隐式 AsQueryable
【发布时间】:2012-06-16 15:50:18
【问题描述】:

以下 linq2entities 代码似乎调用了 FirstOrDefault 扩展的 IEnumerable 版本,因为 PersonH​​istories 是一个 ICollection。然而,在运行时,它实际上调用了 IQueryable 版本。

var test = db.Persons.Where(d => d.PersonKey == 4)
        .Select(f => f.PersonHistories.FirstOrDefault());

我遇到的问题是我使用的自定义查询提供程序不执行此自动转换,并且我收到错误“...ICollection 不能用于 IQueryable 类型的参数”。因此需要显式调用 AsQueryable 来解决这个问题,但对于复杂的查询,它会变得非常多余并且感觉不是很干燥:

db.Persons.Where(d => d.PersonKey == 4)
        .Select(f => f.PersonHistories.AsQueryable().FirstOrDefault());

我在框架参考源中四处寻找试图找到 Linq 2 实体访问者/提供者的东西,但没有运气(也许不是任何开放参考源的一部分)。 基础提供者如何实现对 AsQueryable 的隐式使用?


我明白这些被翻译成表达式树。 我确实了解 Enumerable.FirstOrDefault 被提供商替换为 Queryable.FirstOrDefault 。这是问题的前提。

【问题讨论】:

  • 尝试翻转它,看看它“正常工作”:db.Persons.Where(d => d.PersonKey == 4).PersonHistories.FirstOrDefault()
  • 不,不起作用,因为 .Where 返回一个集合/IQueryable,其中没有这样的 PersonH​​istories 字段。您必须使用 SingleOrDefault 之类的东西来获取单个项目,或者使用 .Select 来投影嵌套字段。我选择使用嵌套投影来演示这个问题,因为这是我遇到问题的地方。由于 db.Persons 是一个 DbSet,因此它是 IQueryable,因此 .Where 自动是 IQueryable 版本,但相比之下,Person.PersonH​​istories 是作为 ICollection 实现的,处理起来更具挑战性。

标签: c# linq entity-framework linq-to-entities


【解决方案1】:

基础提供者如何实现对 AsQueryable 的隐式使用?

他们没有。您的代码真的根本不会执行FirstOrDefault()。它构建了一个表示调用的表达式树,但并不直接执行。查询提供者看到这一点,发现 f.PersonHistories 实际上是基于数据库中的实体,并适当地转换查询。

【讨论】:

  • 那么表达式树是否包含IEnumerableIQueryable 的单独分支?
  • @JoelEtherton:表达式树本身将包含对被调用方法的引用,即Enumerable.FirstOrDefaultQueryable.FirstOrDefault。在这种情况下,树实际上会引用Enumerable.FirstOrDefault,但好的提供者无论如何都会处理它。
  • 我知道它被转换为表达式树,但表达式树实际上包含.Call System.Linq.Queryable.FirstOrDefault(使用默认提供程序时)。但是它怎么知道这样做呢?我的问题是,它如何发现 Queryable 版本而不是 IEnumerable 版本? FirstOrDefault 的 Queryable 版本的代码确实构造了一个表达式,但 IEnumerable 版本不执行任何此类检查。允许它跳跃的机制在哪里?
  • @AaronLS:真的吗?你确定吗?如果f.PersonHistories 没有实现IQueryable,我看不出它会如何。你是怎么确定的?
  • @Jon 我修改了我的帖子。执行语句后,我可以将鼠标悬停在test 变量上并检查表达式调试视图。
猜你喜欢
  • 2014-06-02
  • 2022-01-23
  • 1970-01-01
  • 2018-02-03
  • 1970-01-01
  • 2021-11-05
  • 1970-01-01
  • 2014-08-02
相关资源
最近更新 更多