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

Linq Acrobatics:如何展平分层数据模型?,linq,linq-to-entities,entity-framework-4,Linq,Linq To Entities,Entity Framework 4,我使用这样的SQL来扁平化分层数据。我只是创建了一个视图,然后把它放到EF图上。但是,这不符合“用LinqPad替换SQL Management Studio”的想法。我将如何在Linq(和C#)中编写这些代码?(Linq至实体/实体框架4) 表A包含产品,表B包含多种类别。我想在视图中选择类别id作为单个字段: select A.*, B1.category as color, B2.category as size, B3.category as shape from A left jo

我使用这样的SQL来扁平化分层数据。我只是创建了一个视图,然后把它放到EF图上。但是,这不符合“用LinqPad替换SQL Management Studio”的想法。我将如何在Linq(和C#)中编写这些代码?(Linq至实体/实体框架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#中看到了。我会选择子选择方法

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 designer中创建一些类似这样的奇特实体。

我会选择子选择方法

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 designer中创建一些类似的奇特实体。

不幸的是,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到SQL,只是假设EF类似。)


阿尔宾的答案更具可读性,但可能产生的SQL不太理想。为了与您的SQL完全匹配,您需要将FirstOrDefault替换为DefaultIfEmpty(根据您的数据,可能没有什么区别)。(抱歉,还不能评论;-)

不幸的是,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到SQL,只是假设EF类似。)


阿尔宾的答案更具可读性,但可能产生的SQL不太理想。为了与您的SQL完全匹配,您需要将FirstOrDefault替换为DefaultIfEmpty(根据您的数据,可能没有什么区别)。(抱歉,还不能评论;-)

次要问题-LINQ本身没有明确的外部联接,但是在实体框架中使用LINQ,有很多查询格式会导致外部联接-例如var people=db.Person.Include(p=>p.Address)当Id地址附加在上时,可以为null-EF将其解释为一个左外部联接。次要的一点-LINQ本身没有显式的外部联接,但是在实体框架中使用LINQ,有很多查询格式会导致外部联接-例如var people=db.Person.Include(p=>p.Address)当Id地址附加在上时,可为空-EF将其解释为左外部联接。