此代码引发异常:
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
和最简单的强制转换情况(基本上,它允许提前知道转换逻辑且不可能是自定义用户代码的情况)。
这两个语句之间有一个重要的区别,表明实体框架在这里深入参与。
-
x as Foo
被翻译成 SQL。EF 可以这样做,因为它知道它将始终返回结果,即Foo
或 null。顺便说一下,生成的SQL很可怕,但它可以完成这项工作。 -
(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
子句。