DefaultIfEmpty - LINQ to SQL против памяти - PullRequest
1 голос
/ 12 апреля 2011

Для модульного тестирования мы используем в коллекциях памяти, чтобы проверить логику наших запросов LINQ.Но в приведенном ниже сценарии я вижу различия между результатами LINQ to SQL и 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 имеет значение null.

Исходя из того, как работает DefaultIfEmpty, на самом деле это поведение, которое я ожидал бы.DefaultIfEmpty возвращает один перечисляемый элемент, равный нулю.

Итак, как мне исправить этот код для работы в обоих сценариях?

ОБНОВЛЕНИЕ: Пока я упрощал проблему, я потерялнекоторые важные части информации.Итак, позвольте мне повторить проблему.

У клиента 0-n заказов.Заказ имеет 1-н пунктов.У Товара есть 1-й Заказ.

Мне нужен список Товаров вместе с количеством клиентов, которые заказали этот товар.Если 0 клиентов заказали товар, я бы хотел, чтобы он все еще был возвращен, но с счетом 0.

Проблема заключается в том, что между Орденом и Предметом много-много, что мешает мне использовать синтаксис соединения.

У меня сейчас что-то вроде этого (надеюсь, на этот раз без опечаток):

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 Ответ

1 голос
/ 12 апреля 2011

Ваш запрос начинается с.Это:

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()

Однако, это все еще имеет ту же проблему - o будет нулевым, если нет клиентов для c.CustomerId.Перевод 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() };
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...