此代码抛出异常:
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并且如果任一调用失败,则cast将抛出异常。好的。但是当我运行这段代码时:
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
,因为强制转换可能会调用自定义方法( explicit operator
)LINQ提供商不容易解决这个问题。提供者可能根本无法检查潜在自定义转换的代码(我对表达式树的限制知之甚少),更不用说为底层源转换它了。因此,LINQ to Entities选择仅允许 as
和最简单的转换案例(基本上,它允许提前知道转换逻辑并且不可能是自定义用户代码的情况)。
这两个语句之间有一个重要的区别,它表明实体框架在这里涉及的内容比任何事情(如CLR规则)都多。
x as Foo
被翻译成SQL。 EF可以这样做,因为它知道它总会返回一个结果,或者a Foo
或者为null。顺便说一句,生成的SQL是可怕的,但它完成了这项工作。
(Foo)x
是 不 翻译成SQL。 EF知道没有办法创造 Foo
对象 所有 x
的,作为强制转换语义的要求,它抛出异常。
请注意EF将接受 foos.Select(f => (Bar)f)
,因为始终可以从子类型创建基本类型。实际强制转换是在收到SQL结果后完成的,它不会影响SQL本身。
它也会接受 bars.OfType<Foo>().Select(x => (Foo)x)
(虽然这是相当无用的)。
所以错误信息......
LINQ to Entities仅支持转换EDM原语或枚举类型。
......并非完全正确。 EF确实接受演员阵容。因此,在EF的查询转换器中,只有一堆逻辑来决定是否值得为生成SQL语句而努力。
这与直接投射的工作原理有何不同 as
操作员工作。由于您没有使用值类型,因此无法使用直接转换 - 它仅适用于基元。
现在,你的lambda正试图选择和投射 - 你 可以 把它分成两个操作,所以:
var result = services.SomeQuery(bar).select(x => new Foo() {
SomeProperty = x.SomeProperty,
SomeOtherProperty = x.SomeOtherProperty,
... }).ToList()
至于更多的结果:你错过了一个 where
那里的条款。