Linq Acrobatics:如何展平分层数据模型?

时间:2010-11-18 05:10:05

标签: linq linq-to-entities entity-framework-4

我使用这样的SQL来压缩分层数据。我只是创建一个视图并将其抛在EF图上。但是,这不符合“使用LinqPad替换SQL Management Studio”的心态。我如何在Linq(和C#)中编码? (Linq to Entities / Entity Framework 4)

表A包含产品,表B包含多种类别。我想在视图中选择类别ID作为单个字段:

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等效项?我已经在C#中看到了101 Linq examples

2 个答案:

答案 0 :(得分:2)

我会采用子选择方法。

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设计器中创建一些类似这样的实体。

答案 1 :(得分:2)

不幸的是,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类似。)

Albin的答案更具可读性,但可能产生不太理想的SQL。为了与SQL完全匹配,您需要将DefaultOrDefault替换为DefaultIfEmpty(可能没有区别,具体取决于您的数据)。 (对不起,还不能发表评论; - ))