【问题标题】:Using Expression<Func<>> in a LINQ Query在 LINQ 查询中使用 Expression<Func<>>
【发布时间】:2014-10-28 20:58:51
【问题描述】:

我想定义一个名为CompareProductItemVendorIdsFunc&lt;ProductItemVendor, bool&gt; 过滤器表达式,它可以在我的整个应用程序中使用,主要是在实体框架/LINQ 查询中。

我了解到,为了能够在 LINQ 查询中使用此过滤器,我必须将其声明为 Expression&lt;Func&lt;&gt;&gt; 而不仅仅是 Func&lt;&gt;。我理解这样做的原因,并且我很容易做到这一点。

但我在查询中使用该表达式时遇到以下问题。

一、代码如:

ProductItem.ProductItemVendors.FirstOrDefault(CompareProductItemVendorIds)

注意:ProductItem 是一个数据库实体,它的ProductItemVendors 属性是一个导航集合。

产生错误:

`实例参数:无法从“System.Collections.Generic.ICollection”转换为“System.Linq.IQueryable”

其次,代码如:

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

产生错误:

“CompareProductItemVendorIds”是一个“变量”,但用作“方法”

所以我有我漂亮闪亮的新Expression&lt;Func&lt;&gt;&gt;。如何在我的 LINQ 查询中使用它?

【问题讨论】:

  • 表达式是对函数的描述,而不是函数本身。为什么不能直接使用Func&lt;&gt;
  • @Cameron:因为那时我收到错误The LINQ expression node type 'Invoke' is not supported in LINQ to Entities. 请参阅stackoverflow.com/questions/26617623/using-func-in-linq-query
  • @Seb:在这种情况下,我的理解是它返回查询中的所有项目,然后 然后 在内存中查找匹配项。这是一个非常低效的方法。我试图避免这种情况。
  • ProductItem.ProductItemVendors.FirstOrDefault(CompareProductItemVendorIds) 只是一个表达式,你能给出整个语句吗? (当谈到 EF 时,上下文很重要)
  • @JonathanWood,相信我,确实如此。

标签: c# linq entity-framework


【解决方案1】:

ProductItem 已经是Entity,因此您不能使用您的表达式,您需要使用Compile() 从您的Expression&lt;Func&lt;&gt;&gt; 获取Func&lt;&gt;,因为ProductItemVendors 不再是@987654326 @

ProductItem.ProductItemVendors.FirstOrDefault(CompareProductItemVendorIds.Compile())

您必须像这样在 ProductItemVendorsContext 上使用您的表达式:

var item = Context.ProductItemVendors.FirstOrDefault(CompareProductItemVendorIds);

你不能在查询语法中使用表达式,你需要使用方法 sytanx

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

【讨论】:

  • 返回 Func&lt;T&gt; 将导致使用 Linq-to-Objects 而不是 Linq-to-Entities 执行查询,这在这种情况下是不受欢迎的。
  • 我认为这是一个非常糟糕的方法。我正在使用实体框架,并且表达式需要编译 成 SQL。此建议编译为机器代码,从数据库中加载所有项目,然后过滤内存中的数据。
  • @JonathanWood - 您已经在内存中加载了Entity。您不能同时拥有Entity 及其导航属性的子集。这不是 Entityframework 的工作方式。
  • @Aducci:您可以将ProductItem 加载到内存中,而无需加载每个相关的ProductItemVendor。每当加载一个实体时加载所有相关实体不是 EF 所做的。它会在这里使用延迟加载
  • @JonathanWood - 取决于您的设置方式。我在要加载的导航属性上使用Include。您似乎在使用延迟加载,这意味着您在第一次使用时加载了 ProductItemVendors 导航属性。
【解决方案2】:

我认为默认情况下不支持编写这样的表达式。
你可以试试LINQKit

这是来自 LINQKit 页面的示例;一个 Expression&lt;Func&lt;&gt;&gt; 在另一个里面:

调用 Invoke 调用内部表达式 Call Expand on final 结果。例如:

Expression<Func<Purchase,bool>> criteria1 = p => p.Price > 1000;
Expression<Func<Purchase,bool>> criteria2 = p => criteria1.Invoke (p)
                                                 || p.Description.Contains ("a");

Console.WriteLine (criteria2.Expand().ToString()); 

(Invoke 和 Expand 是 LINQKit 中的扩展方法。)输出如下:

p => ((p.Price > 1000) || p.Description.Contains("a"))

请注意,我们有一个很好的简洁表达式:对 Invoke 的调用有 被剥夺了。

【讨论】:

    【解决方案3】:

    第一种情况;

    ProductItemVendor productItemVendor = ProductItem.ProductItemVendors.FirstOrDefault(CompareProductItemVendorIds);
    

    产生错误:

    `Instance argument: cannot convert from 'System.Collections.Generic.ICollection' to 'System.Linq.IQueryable'
    

    发生是因为它是ICollection,实体已被加载。鉴于dbContextobjectContext 延迟加载与显式加载的实现方式略有不同,但我假设您正在使用显式加载。如果您将加载更改为延迟,ProductItemVendors 的类型将为IQueryable,您正在尝试的操作将会成功。

    鉴于第二种情况,表达式必须可编译为SQL,否则你会得到很多奇怪的错误,可能这里就是这种情况。

    鉴于问题中的信息,很难为您提供更明确的帮助,我无法轻松地重新创建它。如果您可以创建一个 MWE 解决方案并将其上传到某个地方,我可以看看,但恐怕我无法在这里提供更多帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-11
      • 1970-01-01
      相关资源
      最近更新 更多