Linq to Entity 具有多个左外连接

Posted

技术标签:

【中文标题】Linq to Entity 具有多个左外连接【英文标题】:Linq to Entity with multiple left outer joins 【发布时间】:2010-09-09 20:36:35 【问题描述】:

我正在尝试了解 LINQ to Entity 中的左外连接。例如我有以下 3 个表:

公司、公司产品、产品

CompanyProduct 链接到其两个父表 Company 和 Product。

无论给定产品的 CompanyProduct 是否存在,我都想返回所有 Company 记录和关联的 CompanyProduct。在 Transact SQL 中,我将使用左外连接从 Company 表中走出来,如下所示:

SELECT * FROM Company AS C
LEFT OUTER JOIN  CompanyProduct AS CP ON C.CompanyID=CP.CompanyID
LEFT OUTER JOIN  Product AS P ON CP.ProductID=P.ProductID 
WHERE      P.ProductID = 14 OR P.ProductID IS NULL

我的数据库有 3 家公司,2 条 CompanyProduct 记录与 ProductID 14 相关联。因此,SQL 查询的结果是预期的 3 行,其中 2 行连接到 CompanyProduct 和 Product,1 行仅包含 Company CompanyProduct 和 Product 表中的表和空值。

那么,如何在 LINQ to Entity 中编写相同类型的连接来获得类似的结果?

我尝试了一些不同的方法,但语法不正确。

谢谢。

【问题讨论】:

【参考方案1】:

解决了!

最终输出:

theCompany.id: 1  
theProduct.id: 14  
theCompany.id: 2  
theProduct.id: 14  
theCompany.id: 3  

这里是场景

1 - 数据库

--Company Table
CREATE TABLE [theCompany](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [value] [nvarchar](50) NULL,
 CONSTRAINT [PK_theCompany] PRIMARY KEY CLUSTERED 
( [id] ASC ) WITH (
    PAD_INDEX  = OFF, 
    STATISTICS_NORECOMPUTE  = OFF, 
    IGNORE_DUP_KEY = OFF, 
    ALLOW_ROW_LOCKS  = ON, 
    ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY];
GO


--Products Table
CREATE TABLE [theProduct](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [value] [nvarchar](50) NULL,
 CONSTRAINT [PK_theProduct] PRIMARY KEY CLUSTERED 
( [id] ASC
) WITH (    
    PAD_INDEX  = OFF, 
    STATISTICS_NORECOMPUTE  = OFF, 
    IGNORE_DUP_KEY = OFF, 
    ALLOW_ROW_LOCKS  = ON, 
    ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY];
GO


--CompanyProduct Table
CREATE TABLE [dbo].[CompanyProduct](
    [fk_company] [int] NOT NULL,
    [fk_product] [int] NOT NULL
) ON [PRIMARY];    
GO

ALTER TABLE [CompanyProduct]  WITH CHECK ADD CONSTRAINT
    [FK_CompanyProduct_theCompany] FOREIGN KEY([fk_company]) 
    REFERENCES [theCompany] ([id]);
GO

ALTER TABLE [dbo].[CompanyProduct] CHECK CONSTRAINT 
    [FK_CompanyProduct_theCompany];
GO

ALTER TABLE [CompanyProduct]  WITH CHECK ADD CONSTRAINT 
    [FK_CompanyProduct_theProduct] FOREIGN KEY([fk_product]) 
 REFERENCES [dbo].[theProduct] ([id]);
GO

ALTER TABLE [dbo].[CompanyProduct] CHECK CONSTRAINT 
    [FK_CompanyProduct_theProduct];

2 - 数据

SELECT [id] ,[value] FROM theCompany
id          value
----------- --------------------------------------------------
1           company1
2           company2
3           company3

SELECT [id] ,[value]  FROM theProduct
id          value
----------- --------------------------------------------------
14          Product 1


SELECT [fk_company],[fk_product] FROM CompanyProduct;
fk_company  fk_product
----------- -----------
1           14
2           14

3 - VS.NET 2008 中的实体

alt text http://i478.photobucket.com/albums/rr148/KyleLanser/companyproduct.png 实体容器名称是“testEntities”(如模型属性窗口中所示)

4 - 代码(终于!)

testEntities entity = new testEntities();

var theResultSet = from c in entity.theCompany
select new  company_id = c.id, product_id = c.theProduct.Select(e=>e) ;

foreach(var oneCompany in theResultSet)

   Debug.WriteLine("theCompany.id: " + oneCompany.company_id);
    foreach(var allProducts in oneCompany.product_id)
    
        Debug.WriteLine("theProduct.id: " + allProducts.id);
    

5 - 最终输出

theCompany.id: 1  
theProduct.id: 14  
theCompany.id: 2  
theProduct.id: 14  
theCompany.id: 3  

【讨论】:

.Select(e=>e) 是无操作的,可以删除。当然,如果你只使用ID,为什么不说.Select(e => e.id)【参考方案2】:

它应该是这样的......

var query = from t1 in db.table1
    join t2 in db.table2
    on t1.Field1 equals t2.field1 into T1andT2
    from t2Join in T1andT2.DefaultIfEmpty()


    join t3 in db.table3
    on t2Join.Field2 equals t3.Field3 into T2andT3
    from t3Join in T2andT3.DefaultIfEmpty()
    where t1.someField = "Some value" 
    select 
    
        t2Join.FieldXXX
        t3Join.FieldYYY


    ;

我就是这样做的......

【讨论】:

【参考方案3】:

您需要使用实体框架来设置从公司到产品的多对多映射。这将使用 CompanyProduct 表,但无需在实体模型中设置 CompanyProduct 实体。完成此操作后,查询将非常简单,这将取决于个人喜好以及您希望如何表示数据。例如,如果您只想要拥有给定产品的所有公司,您可以说:

var query = from p in Database.ProductSet
            where p.ProductId == 14
            from c in p.Companies
            select c;

var query = Database.CompanySet
            .Where(c => c.Products.Any(p => p.ProductId == 14));

您的 SQL 查询会返回产品信息以及公司。如果这就是你想要的,你可以试试:

var query = from p in Database.ProductSet
            where p.ProductId == 14
            select new
            
                Product = p,
                Companies = p.Companies
            ;

如果您想提供更多信息而不是创建另一个答案,请使用“添加评论”按钮。

【讨论】:

@theKing:我不敢苟同。 OP 问道:“那么你如何在 LINQ to Entity 中编写相同类型的连接来获得类似的结果?”在 LINQ to Entities 中执行此操作的正确方法是 建立多对多关系并通过关系属性访问相关实体。您会看到 OP 的最终解决方案使用了相同的策略。我未能解决问题的哪个方面? 他也找到了解决办法。解决方案在此链接msdn.microsoft.com/en-us/library/bb896266.aspx 中,由 Mitch 下面提供 @theKing:我不同意。使用实体框架时,“组连接”是一种解决方法,当您无法设置上下文以正确建模数据关系时,可以使用该解决方法。在不必要时使用它会产生过多且容易出错的代码。 OP 找到了做到这一点的“正确方法”,而不是满足于解决方法。 好吧,我想我们需要学会不同意。我想知道为什么 Microsoft 和您可以在 Amazon 上找到的所有 LINQ 书籍都建议使用 Group Join?我想他们也一定已经接受了解决方法 @theKing:“LINQ 书籍”不仅仅与实体框架有关。 Microsoft 和所有 LINQ to Entities 书籍和文章都推荐多对多映射。这就是为什么他们对这个特性如此重视(LINQ to SQL 没有)。这也是为什么***.com/questions/1182311/… 是对类似问题的公认“更容易”的答案。【参考方案4】:

LEFT OUTER JOIN 是通过使用实体框架中的 GroupJoin 完成的:

http://msdn.microsoft.com/en-us/library/bb896266.aspx

【讨论】:

【参考方案5】:

普通组连接表示左外连接。试试这个:

var list = from a in _datasource.table1
           join b in _datasource.table2
           on a.id equals b.table1.id
           into ab
           where ab.Count()==0
           select new  table1 = a, 
                        table2Count = ab.Count() ;

该示例为您提供了来自table1 的所有记录,这些记录没有对table2 的引用。 如果省略where 语句,则会得到table1 的所有记录。

【讨论】:

【参考方案6】:

请尝试以下方法:

from s in db.Employees
join e in db.Employees on s.ReportsTo equals e.EmployeeId
join er in EmployeeRoles on s.EmployeeId equals er.EmployeeId
join r in Roles on er.RoleId equals r.RoleId
where e.EmployeeId == employeeId &&
er.Status == (int)DocumentStatus.Draft
select s;

干杯!

【讨论】:

【参考方案7】:

这个呢(在实体设计器中,公司和产品之间确实存在多对多关系,不是吗?):

from s in db.Employees
where s.Product == null || s.Product.ProductID == 14
select s;

实体框架应该能够确定要使用的连接类型。

【讨论】:

以上是关于Linq to Entity 具有多个左外连接的主要内容,如果未能解决你的问题,请参考以下文章

LINQ:具有多个条件的左外连接

左外连接和多重计数 SQL to LINQ

LINQ to SQL 左外连接

Linq to SQL 左外连接不是

使用 Linq to Sql 的左外连接结果问题

Linq To Sql 左外连接 - 过滤空结果