在LINQ中,强制转换与内部选择之间的区别

时间:2015-06-16 19:19:53

标签: c# linq entity-framework

此代码抛出异常:

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(); 

我得到了更大的查询结果。为什么呢?

3 个答案:

答案 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规则)。

  1. x as Foo已翻译成SQL。 EF可以这样做,因为它知道它将始终返回结果,Foo或null。顺便说一句,生成的SQL是可怕的,但它可以完成这项任务。

  2. (Foo)x 已翻译成SQL。 EF知道没有办法为所有 Foo创建x个对象,作为强制转换语义需求,并且它会引发异常。

  3. 请注意,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条款。