在 LINQ 中强制转换和 as 在选择内之间的差异



此代码引发异常:

var query = services
             .SomeQuery(bar).select(x => (Foo)x)
             .Where(x.PropertyOfFoo == FooState.SomeState);
var result = query.ToList(); 

例外情况:

Unable to cast the type...
LINQ to Entities only supports casting EDM primitive or enumeration types.

此代码有效:

var query = services
             .SomeQuery(bar).select(x => x as Foo)
             .Where(x.PropertyOfFoo == FooState.SomeState);
var result = query.ToList(); 

为什么as允许转换而cast不允许转换?

我知道as将返回 null,如果任一调用失败,强制转换将引发异常。好。但是当我运行这段代码时:

var query = services
             .SomeQuery(bar);
var result = query.ToList(); 

我得到一个更大的查询结果。为什么?

LINQ to Entities 与 LINQ to Objects 不同。虽然 LINQ to Objects 函数可以接受任何匹配的委托并将其作为普通 C# 代码盲目调用,但 LINQ to Entities 将 lambda 视为表达式树,因为它需要了解这些 lambda 的语义(而不仅仅是其签名),并将它们转换为 EF 后端的等效查询。当然,这意味着 LINQ to Entities 无法处理 LINQ to Objects 可以处理的所有操作。

现在,正如您所说,强制转换和使用as之间的一个区别是,强制转换会在失败时引发异常,as返回null。但两者之间还有一个更重要的区别:强制转换将应用任何潜在的自定义显式转换,而as将简单地尝试将引用重新解释为其他内容,忽略任何潜在的转换。

如您所知,强制转换比as复杂得多,因为强制转换可能会调用 LINQ 提供程序不容易解析的自定义方法(explicit operator)。提供程序很可能根本无法检查潜在自定义转换的代码(我对表达式树的局限性了解不够),更不用说将其翻译为基础源了。因此,LINQ to Entities 选择仅允许as和最简单的强制转换情况(基本上,它允许提前知道转换逻辑且不可能是自定义用户代码的情况)。

这两个语句之间有一个重要的区别,表明实体框架在这里深入参与。

  1. x as Foo被翻译成 SQL。EF 可以这样做,因为它知道它将始终返回结果,即 Foo 或 null。顺便说一下,生成的SQL很可怕,但它可以完成这项工作。

  2. (Foo)x不会转换为 SQL。EF 知道无法按照强制转换语义的要求为所有x创建Foo对象,并且会引发异常。

请注意,EF 将接受 foos.Select(f => (Bar)f) ,因为始终可以从子类型创建基类型。实际的强制转换是在收到 SQL 结果后完成的,它不会影响 SQL 本身。

它也将接受bars.OfType<Foo>().Select(x => (Foo)x)(尽管认为这是相当无用的)。

所以错误消息...

LINQ to 实体仅支持强制转换 EDM 基元或枚举类型。

。并不完全正确。EF 在可行的情况下接受强制转换。因此,在 EF 的查询转换器中,只有一堆逻辑来确定是否值得努力生成 SQL 语句。

这是

直接强制转换的工作方式与as运算符工作方式的差异。 直接强制转换不可用,因为您没有使用值类型 - 它仅适用于基元。

现在,您的 lambda 正在尝试选择和投影 - 您可以将其分解为两个操作,因此:

var result = services.SomeQuery(bar).select(x => new Foo() {
    SomeProperty = x.SomeProperty,
    SomeOtherProperty = x.SomeOtherProperty,
    ... }).ToList()

至于更多的结果:你在那里缺少一个where子句。

相关内容

  • 没有找到相关文章

最新更新