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

本文关键字:语法 lambda 方法 Where with 子句 查询 Join | 更新日期: 2024-09-22 00:21:54

我简化的LINQ Join加上两个表的Where如下所示:

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属性添加到匿名返回类型中,这样我就可以在之后进行筛选,而在另一个查询中,我仍然可以在where子句中使用foo(如果愿意,还可以使用bar)。如果我不想或不需要,我不需要在这里的返回类型中添加字段foo.Year

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

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

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

但是,与第二条语句相比,这不只是增加了"一个"匿名类型创建吗?还是我错了?

简而言之:第二条语句的等效Join语法是什么?或者,第一个加上添加的Select真的等效吗?也就是说,joinQuery内部是否生成相同的代码?

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

在一般情况下,不能总是像编译器那样在中的查询理解语法和lambda语法之间进行完全的转换。这是由于使用了透明标识符。但是您可以解决这个问题,并生成语义上等效的lambda语句。ReSharper就是这么做的。

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

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

这将为每行实例化一个匿名类型,但不会是"多一个",所以不用担心:表达式被转换为SQL,所以join中的new { foo.Year, foo.Month }语句并没有真正实例化这些对象,它们只是被转换为SQL。只有最后一个选择将同时用于SQL SELECT列表和检索行后的对象水合。

但是,与第二条语句相比,这不只是增加了"一个"匿名类型创建吗?还是我错了?

正如dotctor的回答所示:在这种情况下,当你使用理解语法时,编译器就是这么做的。通过将年份包含在您的单个匿名类型中,您可以稍微减少开销。

但是:

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

记住优化的前两条规则:除非您可以显示真实测试数据中的探查器数据,否则您的性能问题将集中在易于维护的清晰代码上。

另一种选择是将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();

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

对于简而言之:问题的一部分:答案是肯定的
这是重新打磨的结果

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