此代码抛出异常:
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();
我得到了更大的查询结果。为什么呢?
答案 0 :(得分:6)
LINQ to Entities与LINQ to Objects不同。虽然LINQ to Objects functions can take any matching delegate并且盲目地将其作为普通的C#代码调用,LINQ to Entities treats your lambdas as expression trees因为它需要理解这些lambdas的语义(不仅仅是它们的签名)并将它们转换为EF后端的等效查询。当然,这意味着LINQ to Entities无法处理LINQ to Objects可以执行的所有操作。
现在,正如您所说,转换和使用as
之间的一个区别是,转换会在失败时抛出异常并且as
会返回null
。但两者之间存在更为重要的差异:a cast will apply any potential custom explicit conversions, while as
will simply attempt to reinterpret the reference as something else, ignoring any potential conversions.
如您所知,强制转换比as
复杂得多,因为强制转换可能会调用LINQ提供程序无法轻松解析的自定义方法(explicit operator
)。很可能提供商根本无法检查潜在自定义转换的代码(我对表达树的局限性不够了解),更不用说将其转换为底层源代码。因此,LINQ to Entities选择仅允许as
和最简单的转换情况(基本上,它允许提前知道转换逻辑的情况,并且可能不是自定义用户代码)。
答案 1 :(得分:3)
这两个陈述之间存在重要差异,表明实体框架在这里涉及的内容比任何事情都多(如CLR规则)。
x as Foo
已翻译成SQL。 EF可以这样做,因为它知道它将始终返回结果,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语句而努力。
答案 2 :(得分:1)
直接投射的工作方式与as
运算符的工作方式不同。由于您没有使用值类型,因此无法使用直接转换 - 它仅适用于基元。
现在,你的lambda正在尝试选择和投射 - 你可以将其分解为两个操作,所以:
var result = services.SomeQuery(bar).select(x => new Foo() {
SomeProperty = x.SomeProperty,
SomeOtherProperty = x.SomeOtherProperty,
... }).ToList()
关于更多结果:你在那里错过了where
条款。