【问题标题】:LINQ to SQL: Reuse lambda expressionLINQ to SQL:重用 lambda 表达式
【发布时间】:2011-06-01 12:28:01
【问题描述】:

我偶然发现了一些奇怪的 LINQ to SQL 行为 - 任何人都可以对此有所了解吗?

我想定义一个 lambda 表达式并在我的 LINQ 语句中使用它。以下代码工作正常:

[...]
Func<Table1, bool> lambda = x => x.Id > 1000;
var result = dataContext.Table1s.Where(lambda);
[...]

但是当我尝试在关联表的语句中使用我的 lambda 表达式时

[...]
Func<Table1, bool> lambda = x => x.Id > 1000;
var result = dataContext.Table2s.Where(x => x.Table1s.Any(lambda));
[...]

我得到一个例外:

Unsupported overload used for query operator 'Any'.

但是,我不明白:当我将我的 lambda 直接放入查询时它工作正常:

[...]
var result = dataContext.Table2s.Where(x => x.Table1s.Any(y => y.Id > 1000));
[...]

为什么?!

谢谢。

【问题讨论】:

  • 尝试使用var lamda = x =&gt; x.Id &gt; 1000;。不知道会不会有帮助,但可能...
  • @Alxandr - 这实际上是不合法的。 Lambda 表达式可以编译为 Func&lt;&gt;Expression&lt;Func&lt;&gt;&gt;,在您的示例中,编译器将无法判断您想要哪个,并且会抛出错误。

标签: linq-to-sql


【解决方案1】:

好的,这是交易:dataContext.Table1s 的类型为 IQueryable&lt;T&gt;IQueryable&lt;T&gt; 定义了采用Expression&lt;Func&lt;T, bool&gt;&gt; 类型谓词的WhereAny 方法。 Expression&lt;&gt; 包装器至关重要,因为它允许 LINQ to SQL 将您的 lambda 表达式转换为 SQL 并在数据库服务器上执行它。

但是,IQueryable&lt;T&gt; 还包括IEnumerable&lt;T&gt;IEnumerable&lt;T&gt; 还定义了WhereAny 方法,但IEnumerable 版本采用Func&lt;T, bool&gt; 类型的谓词。因为这是一个编译函数而不是表达式,所以它不能被翻译成 SQL。结果,这段代码……

Func<Table1, bool> lambda = x => x.Id > 1000;
var result = dataContext.Table1s.Where(lambda);

...将从Table1s 中提取每条记录到内存中,然后过滤内存中的记录。它可以工作,但如果你的桌子很大,这真的是个坏消息。

Func<Table1, bool> lambda = x => x.Id > 1000;
var result = dataContext.Table2s.Where(x => x.Table1s.Any(lambda));

这个版本有两个 lambda 表达式。第二个,直接传递给Where,是一个Expression,它包含对Func的引用。您不能将两者混为一谈,并且您收到的错误消息是告诉您对 Any 的调用期待的是 Expression,但您传入的是 Func

var result = dataContext.Table2s.Where(x => x.Table1s.Any(y => y.Id > 1000));

在此版本中,您的内部 lambda 会自动转换为 Expression,因为如果您希望通过 LINQ to SQL 将代码转换为 SQL,这是唯一的选择。在其他情况下,您将强制 lambda 为 Func 而不是 Expression - 在这种情况下您不是,所以它可以工作。

解决办法是什么?其实很简单:

Expression<Func<Table1, bool>> lambda = x => x.Id > 1000;

【讨论】:

  • 谢谢。不幸的是它不会编译,编译器错误是: 参数 2: cannot convert from 'System.Linq.Expressions.Expression>' to 'System.Func'跨度>
  • 该错误消息似乎表明您在实现 IEnumerable 但不是 IQueryable 的对象上调用 WhereAny - 因为 IEnumerable 版本需要一个 Func,其中 @ 987654322@ 版本需要一个表达式。如果它不接受表达式,则该对象可能不是 IQueryable...
  • 它被定义为 EntitySet,由 SQLMetal/Visual Studio 设计器为 LINQ to SQL 表关联生成。
  • This article 可能对您的情况有所帮助...
  • 感谢您的链接,乔尔。它并没有解决我上面的问题,但它还是非常有用的,给了我一些新的想法:)
【解决方案2】:

Table1 指的是同一个命名空间吗?在第一个示例中,您查询的是直接在dataContext 下的Table1 对象,在第二个示例中,您查询的是Table1 对象,它是Table2 对象的属性,并且在最后一个示例,您使用的是解决问题的匿名函数。

我会查找作为Table2 对象属性的Table1 对象的类型,并将其与直接连接到dataContextTable1 对象进行比较。我的猜测是它们不同,并且您的 lambda 表达式使用的是连接到 dataContext 的对象的类型。

【讨论】:

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