【问题标题】:Strange Linq to Entities Behavior with AsExpandable()使用 AsExpandable() 的奇怪 Linq 到实体行为
【发布时间】:2025-12-29 02:00:12
【问题描述】:

考虑以下Person 实体:

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

使用以下Expression(使用PredicateBuilder构造)作为条件:

var byName = PredicateBuilder.True<Person>().And(x => x.FirstName == "Chaim");

使用以下语法调用时,生成的 SQL 很好(包括 WHERE 语句):

ctx.Set<Person>().AsExpandable().Where(x => byName.Invoke(x));

但是,当使用这种略有不同的语法调用时,不涉及 SQL WHERE,而是由 Enumerable.Where 完成过滤:

ctx.Set<Person>().AsExpandable().Where(byName.Invoke);

有什么想法吗?

【问题讨论】:

  • 这是非常危险的。我打电话给Where(expression.Compile()) 而不是Where(x =&gt; expression.Invoke(x))。然后,在跟踪我们的生产数据库是否很糟糕时,我看到了select [every column] from Table;没有 where 子句。前者来自文档!

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


【解决方案1】:

没有从方法组到Expression(对应的委托类型)的隐式转换。 从方法组到匹配签名的委托的隐式转换。因此只有 IEnumerable 重载匹配。

当然,这并不是说您需要使用 lambda。随便写:

ctx.Set<Person>().AsExpandable().Where(ByName);

由于您要传入一个表达式(ByName 毕竟已经是一个Expression&lt;Person, bool&gt;,这正是Queryable.Where&lt;Person&gt; 所要求的),这将作为一个查询进行评估,而不是在 linq 中对对象进行评估。

【讨论】:

  • 有趣。所以你基本上是说ByName.InvokeFunc&lt;Person, bool&gt; 然后只匹配IEnumerable.Where,而x =&gt; ByName.Invoke(x)Expression&lt;Func&lt;Person, bool&gt;&gt; 然后匹配IQueryable.Where
  • @haim770 x =&gt; ByName.Invoke(x),作为一个 lambda,可以匹配 either。它可以是一个表达式,也可以是一个委托。正因为如此,Queryable.WhereEnumerable.Where 都是有效的选项,并且它转向更好的算法。 Queryable 获胜。 ByName.Invoke,只是一个方法组,与任何其他方法组一样,不能转换为Expression,因此Enumerable.Where 是唯一可能的有效重载。
  • 我明白了。选择“更好的算法”是在运行时执行的?怎么样?
  • @haim770 它在编译时完成。有关详细信息,请参阅重载解析规范。