为什么Linq的加入方式不同

时间:2009-02-11 14:32:19

标签: c# linq

好的,这是一个小小的呻吟,但这也是一个问题。在Linq,我可以这样做:

from c in dc.Customers join o in dc.Orders on c.custid equals o.custid ...

一切都很好,完全可以记住,而不必回去谷歌吧。然而,由于某种原因,左连接要复杂得多:

from c in dc.Customers 
join o in dc.Orders on c.custid equals o.custid 
into temp from x in temp.DefaultIfEmpty() ...

所以我的问题是为什么Linq的设计师不能用这样的东西简单(更像sql):

来自c中的c。来自c.custid等于o.custid的dc.Orders中的连接o ...

干杯 利

2 个答案:

答案 0 :(得分:5)

  

为什么Linq的设计师不能简单(更像sql)

他们可以。但是你对简单(作为sql程序员)的定义与OO程序员对简单的定义不同。 Linq(在C#中)首先是面向OO程序员的查询技术。一个例子是为什么选择最后。这是在编辑器中实现C#中的范围规则和智能感知支持。

这些程序员可能没有得到LEFT JOIN(如果你说LEFT OUTER JOIN会变得非常困惑 - 认为存在一些差异,比如一个继承了另一个)。

他们理解的是GROUP JOIN,其行为方式类似。

List<int> myInts = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
List<int> myOtherInts = new List<int>() { 1, 3, 5, 7, 9, 11, 13 };
//
var query = from i in myInts
    join j in myOtherInts on i equals j into g
    select new {key = i, myGroup = g};
//
foreach (var grouping in query)
{
  Console.WriteLine("--{0}", grouping.key);
  foreach (var x in grouping.myGroup)
    Console.WriteLine(x);
}

DefaultIfEmpty所做的所有操作都是将组解压缩 - 将结果展平为行/列形式 - 远离OO程序员的自然层次表。获得结果在语义上不需要DefaultIfEmpty

这是方法形式的相同查询 - 编译器从上面生成并且我更喜欢它:

var query = myInts.GroupJoin(
    myOtherInts,
    i => i,
    j => j,
    (i, g) => new { key = i, myGroup = g }
);

  

你能说出他的榜样吗?

此查询为您的客户提供他们的订单作为附加集合。订单集合可能为空。如果您有50个客户和1000个订单,则结果中将有50个客户。

from c in dc.Customers
join o in dc.Orders on c.custid equals o.custid into someOrders
select new CustomerWithOrders()
  {theCustomer = c, theOrders = someOrders};

此查询为您提供CustomerOrder行。如果客户有5个订单,则客户将出现5次,每次都与不同的订单匹配。如果客户有0个订单,则客户将出现一次与空订单匹配。如果您有50个客户和1000个订单,则在连接后将有50-1049行,并且结果中的元素含义很难定义。

from c in dc.Customers
join o in dc.Orders on c.custid equals o.custid into temp
from x in temp.DefaultIfEmpty()
select new CustomerOrderHybrid() {theCustomer = c, theOrder = x}

如果他们实现了left join,则需要第二个示例的结果形状。一旦我使用group join,这是更好的,我也不会一步实现left join。查询结果的分层整形很棒。

答案 1 :(得分:0)

可能是因为Linq表达式在编译器中只是syntactic sugar,它将它们转换为方法调用。因此,查询语法是面向对象系统的漏洞抽象。

由于您实际上并没有编写SQL,因此必然会出现底层技术行为不同的情况。添加类似SQL的“左连接”可能比你想象的要难得多。


有些人显然不知道Linq表达式是如何工作的,所以这里有一个进一步的解释。

如果我参加这个考试课程:

public class Class1
{
    public List<string> list = new List<string>() { "test", "test1", "test2" };

    public void test_lambda()
    {
        var test = list.Where(l => l == "test1");
    }

    public void test_linq()
    {
        var test = from l in list
                   where l == "test2"
                   select l;
    }
}

list.Where(l => l == "test2")编译为与from l in list where l == "test2" select l相同的代码。在这两种情况下,编译器都会生成匿名方法委托:

.method public hidebysig instance void test_lambda() cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: ldfld class [mscorlib]System.Collections.Generic.List`1<string> Class1::list
    L_0006: ldsfld class [System.Core]System.Func`2<string, bool> Class1::CS$<>9__CachedAnonymousMethodDelegate1
    L_000b: brtrue.s L_001e
    L_000d: ldnull 
    L_000e: ldftn bool Class1::<test_lambda>b__0(string)
    L_0014: newobj instance void [System.Core]System.Func`2<string, bool>::.ctor(object, native int)
    L_0019: stsfld class [System.Core]System.Func`2<string, bool> Class1::CS$<>9__CachedAnonymousMethodDelegate1
    L_001e: ldsfld class [System.Core]System.Func`2<string, bool> Class1::CS$<>9__CachedAnonymousMethodDelegate1
    L_0023: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Where<string>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [System.Core]System.Func`2<!!0, bool>)
    L_0028: pop 
    L_0029: ret 
}

.method public hidebysig instance void test_linq() cil managed
{
    .maxstack 8
    L_0000: ldarg.0 
    L_0001: ldfld class [mscorlib]System.Collections.Generic.List`1<string> Class1::list
    L_0006: ldsfld class [System.Core]System.Func`2<string, bool> Class1::CS$<>9__CachedAnonymousMethodDelegate3
    L_000b: brtrue.s L_001e
    L_000d: ldnull 
    L_000e: ldftn bool Class1::<test_linq>b__2(string)
    L_0014: newobj instance void [System.Core]System.Func`2<string, bool>::.ctor(object, native int)
    L_0019: stsfld class [System.Core]System.Func`2<string, bool> Class1::CS$<>9__CachedAnonymousMethodDelegate3
    L_001e: ldsfld class [System.Core]System.Func`2<string, bool> Class1::CS$<>9__CachedAnonymousMethodDelegate3
    L_0023: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::Where<string>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>, class [System.Core]System.Func`2<!!0, bool>)
    L_0028: pop 
    L_0029: ret 
}

这就是我的意思是语法糖。查询表达式不会为语言添加任何新内容,它们只是提供了一种使用现有语言功能的简便方法。