Linq Acrobatics: Как сгладить иерархические модели данных? - PullRequest
0 голосов
/ 18 ноября 2010

Я использую SQL таким образом, чтобы сгладить иерархические данные. Я просто создаю представление и добавляю его на диаграмму EF. Однако это не соответствует менталитету «Замените SQL Management Studio на LinqPad». Как бы я кодировал их в Linq (и C #)? (Linq to Entities / Entity Framework 4)

Таблица A содержит товары, а таблица B содержит множество категорий. Я хочу выбрать идентификатор категории в виде одного поля в представлении:

select A.*, B1.category as color, B2.category as size, B3.category as shape
from   A left join B B1 on A.key = B1.key and B1.type = 1 -- Selects one B row
         left join B B2 on A.key = B2.key and B2.type = 2
         left join B B3 on A.key = B3.key and B3.type = 3

Еще лучше, есть ли поваренная книга шаблонов Linq, где вы можете найти SQL и увидеть эквивалент Linq? Я уже видел 101 примеры Linq в C #.

Ответы [ 2 ]

2 голосов
/ 19 ноября 2010

К сожалению, в LINQ нет ни внешнего соединения, ни добавления произвольных условий соединения. Внутреннее соединение можно обойти, используя DefaultIfEmpty, но часть условия соединения Bn.type = n должна быть перемещена в условие where.

Следующий код производит именно тот код SQL, который вы указали, за исключением указанных выше типов:

from A in products
join B1 in categories on A.key equals B1.key into tmp_color
join B2 in categories on A.key equals B2.key into tmp_size
join B3 in categories on A.key equals B3.key into tmp_shape
from B1 in tmp_color.DefaultIfEmpty()
from B2 in tmp_size.DefaultIfEmpty()
from B3 in tmp_shape.DefaultIfEmpty()
where B1.type == 1 && B2.type == 2 && B3.type == 3
select new { product = A, color = B1.category, size = B2.category, shape = B3.category };

Результаты в

exec sp_executesql N'SELECT [t0].[key], [t1].[category] AS [color], [t2].[category] AS   [size], [t3].[category] AS [shape]
FROM [Product] AS [t0]
LEFT OUTER JOIN [Category] AS [t1] ON [t0].[key] = [t1].[key]
LEFT OUTER JOIN [Category] AS [t2] ON [t0].[key] = [t2].[key]
LEFT OUTER JOIN [Category] AS [t3] ON [t0].[key] = [t3].[key]
WHERE ([t1].[type] = @p0) AND ([t2].[type] = @p1) AND ([t3].[type] = @p2)',N'@p0 int,@p1 int,@p2 int',@p0=1,@p1=2,@p2=3

(Обновление: это LINQ to SQL, только если предположить, что EF будет похожим.)

Ответ Альбина более читабелен, но, вероятно, дает менее оптимальный SQL. Для точного соответствия с вашим SQL вам нужно заменить FirstOrDefault на DefaultIfEmpty (может не иметь значения, в зависимости от ваших данных). (Извините, пока не могу комментировать; -))

2 голосов
/ 18 ноября 2010

Я бы выбрал подход, не связанный с выбором.

from a in ModelEntities.A
select new
{
    f1 = a.f1,
    f2 = a.f2,
    // ...,
    fn = a.fn,
    color = ModelEntities.B.Where(b => a.key == b.key && b.type == 1)
                           .Select(b => b.category).FirstOrDefault(),
    size = ModelEntities.B.Where(b => a.key == b.key && b.type == 2)
                          .Select(b => b.category).FirstOrDefault(),
    shape = ModelEntities.B.Where(b => a.key == b.key && b.type == 3)
                           .Select(b => b.category).FirstOrDefault(),
}

Но, следуя привычке создания представления, вы, вероятно, должны создать в EF-конструкторе какую-то причудливую сущность, которая делает что-то подобное.

...