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

Inv*_*nda 12 .net c# linq linq-to-entities entity-framework

我简化的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();
Run Code Online (Sandbox Code Playgroud)

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

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();
Run Code Online (Sandbox Code Playgroud)

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

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

我其实可以做什么(使上发言在结构上与下一个更相似)是添加的以下行Where(..),并ToList()在第一个:

.Select(anon => new { /* the properties I want */ })
Run Code Online (Sandbox Code Playgroud)

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

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

Luc*_*ski 6

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

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

.Select(anon => new { /* the properties I want */ })
Run Code Online (Sandbox Code Playgroud)

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


Ric*_*ard 5

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

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

然而:

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

请记住前两个优化规则:除非您能够显示 - 来自真实测试数据的探查器数据 - 否则您的性能问题将集中在易于维护的清晰代码上.