2011-04-12 67 views
0

对于我们在内存集合中使用的单元测试来验证LINQ查询的逻辑。但是,在下面的场景中,我看到了LINQ to SQL vs In Memory的结果之间的差异。DefaultIfEmpty - LINQ to SQL vs In Memory

对于这个例子,我们有三个表Customer,Order,Item。我希望客户订购的所有物品的数量。我想向那些还没有订购任何物品的顾客展示。在SQL中,这将是一个外连接。在LINQ to SQL中,我写了这个...

var itemCounts = 
    from c in Customer 
    from o in Order.Where(oo=>o.CustomerId==c.CustomerId).DefaultIfEmpty() 
    from i in Item.Where(ii=>i.OrderId==o.OrderId).DefaultIfEmpty() 
    group i by new { i.ItemId, c.CustomerId } into ig 
    select new ItemCountResult { 
    CustomerId = ig.Key.CustomerId, 
    Count = ig.Count() 
    }; 

这对我们违背数据库时正常工作。我们获得客户的订单和没有订单的数量。当我们将内存集合替换为单元测试时,我们看到对象引用未设置异常。我已经缩小到“i.OrderId == o.OrderId”这一行,具体说o是空的。

根据“DefaultIfEmpty”的工作方式,这实际上是我期望的行为。 DefaultIfEmpty返回一个枚举为null的单个元素。

那么我该如何解决这段代码在两种情况下工作?

更新: 虽然我简化了问题,但我丢失了一些重要的信息。所以让我重申一下这个问题。

客户有0-n个订单。 订单有1-n个项目。 一个项目有1-n个订单。

我需要项目列表以及订购该项目的客户数量。如果0位顾客订购了我想要的产品,但它仍然返回,但计数为0.

问题是Order和Item之间的多对多阻止我使用join-into语法。

我现在有这样的事(希望没有输入错误时这段时间):

var counts = 
    from i in Items 
    from oi in OrderItems.Where(z=>z.ItemId==i.ItemId).DefaultIfEmpty() 
    from o in Orders.Where(z=>z.OrderId==oi.OrderId).DefaultIfEmpty() 
    from c in Customers.Where(z=>z.CustomerId==o.CustomerId).DefaultIfEmpty() 
    group c by new { i.ItemId, c.CustomerId } into cg 
    select new CountResult { 
    CustomerId = cg.Key.CustomerId, 
    Count = cg.Count() 
    }; 

回答

1

您的查询pooched开始。这:

from ... 
from o in Order.Where(oo=>o.CustomerId==c.CustomerId).DefaultIfEmpty() 
from i in Item.Where(ii=>i.OrderId==o.OrderId).DefaultIfEmpty() 

...试图使用o它是真正的范围之前。我很惊讶,完全可以工作。它看起来像你想:

from ... 
from o in Order.Where(oo => oo.CustomerId == c.CustomerId).DefaultIfEmpty() 
from i in Item.Where(ii => ii.OrderId == o.OrderId).DefaultIfEmpty() 

然而,仍然有同样的问题 - 如果没有客户c.CustomerIdo将是无效的。 SQL翻译可能不会表现出相同的行为,但坦率地说,以IMO开始有点奇怪。

试试这个,假设你有正确的关系成立:

from c in Customer 
join i in Items on c.CustomerId equals i.Order.OrderId into items 
select new { CustomerId = c.CustomerId, Count = items.Count() }; 

这里的另一种选择,返回到使用显式连接:

from c in Customer 
join oi in (from o in Orders 
      join i in Items on o.OrderId equals i.OrderId 
      select new { o, i }) 
on c.CustomerId equals oi.o.CustomerId into ordersForCustomer 
select new { CustomerId = c.CustomerId, Count = ordersForCustomer.Count() }; 
+0

当我简化我拼写错误的O问题vs oo。我也遗漏了我们的关系。 Order to Item实际上是多对多而不是一对多的。抱歉。你是正确的,但交替的加入语法可以解决问题。但是我无法在多对多的情况下工作。我会在上面更新我的问题。 – joegtp 2011-04-12 17:25:01