Join with Where子句的查询和方法(lambda)语法的等价性

时间:2015-03-03 08:25:18

标签: c# .net entity-framework linq linq-to-entities

我的两个表的简化LINQ JoinWhere如下所示:

var join = context.Foo
  .Join(context.Bar,
    foo => new { foo.Year, foo.Month },
    bar => new { bar.Year, bar.Month },
    (foo, bar) => new { foo.Name, bar.Owner, foo.Year })
  .Where(anon => anon.Year == 2015).ToList();

或者我可以使用以下语法,我希望它是等效的:

var joinQuery = from foo in context.Foo
                join bar in context.Bar
                on new { foo.Year, foo.Month } equals new { bar.Year, bar.Month }
                where foo.Year == 2015
                select new { foo.Name, bar.Owner };
var join = joinQuery.ToList();

我和我想知道的一个区别是命令的顺序。在lambda语法连接中,我将foo.Year属性添加到我的匿名返回类型,以便我可以在之后进行过滤,而在另一个查询中,我仍然可以使用foo(和bar如果我我希望在where条款中。如果我不想要或不需要,我不需要在此处将foo.Year字段添加到我的返回类型中。

不幸的是,我没有ReSharper或任何类似的东西可以将较低的语句转换为lambda语句,以便我可以进行比较。

我实际上可以做的事情(并使上层语句在结构上与下层语句更相似)是在第一行Where(..)ToList()之间添加以下行:

.Select(anon => new { /* the properties I want */ })

但是,这不仅仅是添加一个"匿名类型创建与第二个语句相比,还是我在这里弄错了?

简而言之:第二个语句的等效Join语法是什么?或者是第一个加上Select真正等效的,即joinQuery内部产生相同的代码吗?

4 个答案:

答案 0 :(得分:6)

在一般情况下,您不能总是在完全中以与编译器相同的方式在查询理解语法和lambda语法之间进行转换。这是由于transparent identifiers的使用。但是你可以解决这个问题并生成语义上等效的 lambda语句。这就是ReSharper所做的。

无论如何,在你的情况下,你可以添加:

.Select(anon => new { /* the properties I want */ })

这将在每行实例化一个匿名类型,但它不会再"还有一个" ,所以不要担心:表达式转换为SQL ,因此new { foo.Year, foo.Month }中的join语句并不真正实例化这些对象,它们只是转换为SQL。只有最后一个选择将用于SQL SELECT列表,并且一旦检索到行就进行对象水合。

答案 1 :(得分:5)

  

但是,这不仅仅是添加一个"匿名类型创建与第二个语句相比,还是我在这里弄错了?

正如dotctor的答案所示:这是在这种情况下使用理解语法时编译器正在执行的操作。通过在您的单一匿名类型中包含年份,您可以略微减少开销。

然而:

  • 匿名类型非常轻量级:使用泛型,并且通过引用类型共享实现的泛型共享实现意味着代码很少(在反编译器中查看程序集)。
  • 虽然创建了大量实例,但在大多数情况下,它们会在第0代被清理,因为它们几乎立即被释放。
  • C#编译器和JIT的优化工具有很多可以在这里工作。很可能采用快捷方式(但是您需要从正在运行的进程中读取x86 / x64程序集才能看到)。

请记住前两个rules of optimisation:除非您可以显示 - 来自真实测试数据的探查器数据 - 否则您会将性能问题集中在易于维护的清晰代码上。

答案 2 :(得分:3)

另一种方法是在where方法之前移动join方法,然后将年份从匿名类中删除:

var join = context.Foo
    .Where(foo => foo.Year == 2015)
    .Join(context.Bar,
        foo => new { foo.Year, foo.Month },
        bar => new { bar.Year, bar.Month },
        (foo, bar) => new { foo.Name, bar.Owner })
    .ToList();

但总的来说,其他答案是正确的,因为没有太大的区别,编译器可以为你处理细节。

答案 3 :(得分:1)

简而言之:部分问题:答案是肯定的 这里是resharpers结果

var joinQuery = context.Foo.Join(context.Bar, foo => new
{
    foo.Year,
    foo.Month
}, bar => new
{
    bar.Year,
    bar.Month
}, (foo, bar) => new
{
    foo,
    bar
}).Where(@t => @t.foo.Year == 2015).Select(@t => new
{
    @t.foo.Name,
    @t.bar.Owner
});