【问题标题】:Using Func<> in LINQ Query在 LINQ 查询中使用 Func<>
【发布时间】:2023-03-22 00:34:02
【问题描述】:

我有一个Func&lt;ProductItemVendor, bool&gt; 存储在CompareProductItemVendorIds 中。我想在 LINQ 查询中使用该表达式。

看来以下是合法的:

var results =
    Repository.Query<ProductItemVendor>().Where(CompareProductItemVendorIds);

但是,以下内容是不合法的:

var results = from v in Repository.Query<ProductItemVendor>()
              where CompareProductItemVendorIds(v)
              select v;

此代码产生错误:

LINQ to Entities 不支持 LINQ 表达式节点类型“Invoke”。

问题:

  1. 为什么这些声明如此不同,以至于我的Func&lt;&gt; 在其中一个是合法的,而另一个是不合法的?我认为他们基本上都做了同样的事情。

  2. 我怎样才能做到这一点?我是否必须明确地将我的Func&lt;&gt; 创建为Expression&lt;Func&lt;&gt;&gt;

请在Using Expression<Func<>> in a LINQ Query查看我的相关问题。

【问题讨论】:

  • 一个菜鸟问题:第一个示例中的 CompareProductItemVendorIds 是否与第二个示例中的 CompareProductItemVendorIds(v) 相同?
  • @Kapol:是的,那是我的Func&lt;&gt; 的存储位置,我在第一段中对此进行了描述。
  • 查看LINQKit 以帮助您从表达式中编写查询。

标签: c# linq func


【解决方案1】:

Expression&lt;Func&lt;T,bool&gt;&gt;Func&lt;T,bool&gt; 之间存在很大差异。第一个是表达式树。您可以将其视为代码的描述。 Linq to Entities 需要表达式树。因为它需要构建 SQL 查询。所以它需要代码描述才能将相同的操作转换为 SQL。

第二个,Func&lt;T,bool&gt; 是一个带有指定签名的简单方法。这里没什么特别的。为什么它在这里合法:

Repository.Query<ProductItemVendor>().Where(CompareProductItemVendorIds);

这很简单。有两种Where 扩展方法。一个前IQueryable&lt;T&gt;,它需要表达式树(将被翻译成SQL 查询)。另一个是IEnumerable&lt;T&gt; 的扩展,它需要内存中集合过滤的序数方法(通常的 C# 代码)。因此,您没有表达式树,选择了后者。这里没有生成 SQL。这是你的情况。

现在第二个查询:

from v in Repository.Query<ProductItemVendor>()
where CompareProductItemVendorIds(v)
select v

其实这不是同一个查询。相当于

Repository.Query<ProductItemVendor>().Where(v => CompareProductItemVendorIds(v));

这里有 lambda 表达式,可以转换成表达式树。并且使用了另一个Where 扩展名- 一个用于IQueryable&lt;T&gt;。因此,Linq to Entities 尝试将此表达式树转换为 SQL。但它应该转换什么?是的,调用一些内存中的方法。当然,Linq to Entities 无法做到这一点。

为了使您的查询正常工作,您应该使用Expression&lt;Func&lt;T,bool&gt;&gt;。您可以手动构建它,也可以使用 lambda 表达式。

【讨论】:

  • 谢谢。是的,我知道Func&lt;&gt;Expression&lt;Func&lt;&gt;&gt; 之间的区别。你是说,第一种情况,先物化列表,再物化列表上调用Where()
  • @JonathanWood 是的,完全正确。您将所有数据放入内存,然后过滤内存中的集合。您可以使用 EF 或 SQL 分析器检查它
  • @JonathanWood 是的,当您在 IQueryable 对象上调用 Enumberable.Where 时,它将在执行 where 之前具体化结果。要首先不实现列表,您必须调用Queryable.Where,您可以看到它只接受一个表达式。
  • 最后一个问题:我现在有一个Expression&lt;Func&lt;&gt;&gt;,但我似乎找不到任何方法将它合并到我的 LINQ 查询中。 注意:我没有使用.Where() 语法。我的查询使用from v in x where ... 格式。
【解决方案2】:

您的第一个版本有效的原因是 Linq 太聪明了,不适合它自己。相当于你的第一个版本实际上是

IEnumerable<ProductItemVendor> temp = Repository.Query<ProductItemVendor>().AsEnumerable();
var results = temp.Where(CompareProductItemVendorIds);

因此,当您执行查询时,您将返回数据库中的每一行,然后在本地计算机的内存中执行 Where

要在数据库上执行 Where 子句,您必须将 CompareProductItemVendorIds 的类型更改为 Expression&lt;Func&lt;ProductItemVendor, bool&gt;&gt;

没有办法从Func&lt;TIn, TOut&gt;“转换”为Expression&lt;Func&lt;TIn. Tout&gt;&gt;,您必须将代码重写为最初是一个表达式,然后您可以通过调用CompareProductItemVendorIds.Compile()转换为Func&lt;TIn, TOut&gt;

Expression<Func<ProductItemVendor, bool>> CompareProductItemVendorIds = //...
Func<ProductItemVendor, bool> CompareProductItemVendorIdsAsFunc = CompareProductItemVendorIds.Compile();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多