使用LINQ表达式转换时出现“内部.NET Framework数据提供程序错误1025”。

5
以下代码:
model.Items = dbQuery.Select(c => new Item {
    Total = c.Items.Count((Func<Item, bool>)(t => t.Amount.HasValue))
}).ToArray();

抛出以下异常:

内部 .NET Framework 数据提供程序错误 1025。

但是去掉转换的确切代码有效:

model.Items = dbQuery.Select(c => new Item {
    Total = c.Items.Count(t => t.Amount.HasValue)
}).ToArray();

注意:dbQuery 只是一个 EF ObjectQuery

Count() 方法的期望类型为 Func<Item, bool>,并且 ReSharper 验证它们是相同的类型,这通过它将强制转换变灰来表示,暗示不需要它们也可以达到相同功能。
然而,异常证明它们实际上是功能等价的。
此外,我看到了这个相关的问题/答案,但是 Count()(在这种情况下)不会接受 Expression<Func<Item, bool>>,所以对我没有用。

1
那么为什么你要试图添加一个被设计成无用的东西,但这导致你的代码完全无法工作。如果你做那件事会感到痛苦,就不要做那件事。 - Servy
你尝试过转换为 Espression<Func<Item,bool>> 吗? - Alexander Galkin
@AlexanderGalkin他在问题中已经回答了这个问题。 - Servy
@Servy 我试图从细节中抽象出实际问题,以求简单。但归根结底,我正在尝试重用我的(更复杂的)表达式中的逻辑,并将此逻辑存储在 Func<Item, bool> 中并不起作用。但如果我能让上面的代码工作,那么我就可以解决我更复杂的实际问题。 - Jerad Rose
@JeradRose 不行,你不能这样做。你提供的代码理论上是可行的,如果查询提供程序足够强大,它就可以正常工作。但是,将代码从表达式中提取出来并存储在委托中实际上会从表达式中删除信息,使得无论其编写得多么强大,查询提供程序都无法访问该信息。当然,这并不意味着不可能将表达式分成多个表达式,因为它是可以的,只是你不能通过将代码放入委托中来实现。 - Servy
2个回答

11

您想要“将表达式存储在变量中,以便重复使用,并将其传递给Count()”。如果Expression可以转换为SQL,则完全可以做到这一点。问题在于,您引入了无法转换为SQL的内容。在这种情况下,特定的问题是将转换转换为Expression.Convert。您的类型肯定没有在SQL中表示,因此无法执行此操作。将它们都插入到LINQPad中并检查IL以查看差异。

正如您提到的其他相关问题/答案建议的那样,您需要将谓词作为Expression而不是Func传递(如何执行工作与指向执行工作的代码位置)。问题在于,如您所指出的,Count方法不接受Expression,但这很容易解决,只需使用AsQueryable()即可获得接受ExpressionIQueryable扩展方法。

这应该没有执行问题:

Expression<Func<Item,bool>> predicate = i => i.Amount.HasValue;

model.Items = dbQuery.Select(c => new Item {
    Total = c.Items.AsQueryable().Count(predicate)
}).ToArray();

1
很确定查询提供程序不知道如何处理 AsQueryable 调用。 - Servy
我也没想到,但它确实可以(至少在EF6 -> MSSQL2008中)。感谢问题OP链接的评论者。 - Ocelot20
它生成适当的 SQL 代码,而不仅仅在应用程序侧执行工作? - Servy
是的,也已经确认了。 - Ocelot20

1
问题在于数据提供程序无法将LINQ表达式转换为SQL语句。在不起作用的那个中,Count被传递了一个指向方法的委托。可以将它视为像传递指向方法的指针一样:
private static bool MyFunction(Item item)
{
    return item.Amount.HasValue; 
}

现在的问题是数据提供程序不会进入该方法并尝试将其转换为SQL。
数据提供程序非常有能力遍历表达式树来构建SQL,这就是“减去转换”示例和您帖子中链接的相关答案的工作原理。
如果您可以更新您的帖子,说明您想要实现什么,我可能能够帮助您。

非常好的解释,谢谢。你可能已经回答了我的问题:这是不可能的。基本上我正在尝试做你说我不能做的事情:将我的表达式存储在一个变量中,以便重复使用,并将其传递到Count()函数中。 - Jerad Rose
实际上,它是一个表达式。它是一个代表委托创建的表达式,因此所有信息都在表达式中,并且理论上可以将其转换为SQL。查询提供程序只是不希望进行这个特定的操作,所以它会出错,但这是因为他们没有预料到它并编写代码来解决它,而不是因为他们需要的信息不存在。 - Servy
@JeradRose,那么你肯定做了一些在你的问题中没有显示出来的事情,因为在你的问题中根本没有这样的操作。 - Servy
@JeradRose 你得到相同的异常并不是真正相关的。在一个情况下,查询提供程序可以解决问题,但选择不这样做,在另一个情况下,它是一个无论如何都无法解决的不可能问题。错误消息相同并不改变这两种情况下问题完全不同的事实。 - Servy
@MikeHixson 如果你在问题内引用了一个独立的 Func 变量,那么这是正确的。但是,当 Func 的值在表达式本身中定义时,就像在问题中一样,那么表达式本身不仅具有函数的 IL,还有一个表示用于创建 Func 的 C# 代码的实际表达式,查询提供程序完全能够将其转换为 SQL 代码而不触及任何 IL 代码。将代码从 Expression 中提取出来并放入变量中会改变事情的发展。 - Servy
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接