Linq to Entity с несколькими левыми внешними соединениями - PullRequest
20 голосов
/ 17 сентября 2008

Я пытаюсь понять левые внешние соединения в LINQ to Entity. Например, у меня есть следующие 3 таблицы:

Компания, КомпанияПродукт, Продукт

CompanyProduct связан с двумя родительскими таблицами, Company и Product.

Я хочу вернуть все записи Компании и связанный Продукт Компании, независимо от того, существует Продукт Компании или нет для данного продукта. В 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, чтобы получить похожий результат?

Я пробовал несколько разных вещей, но не могу получить правильный синтаксис.

Спасибо.

Ответы [ 7 ]

16 голосов
/ 06 октября 2008

Решил!

Окончательный результат:

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

альтернативный текст 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  
6 голосов
/ 18 марта 2011

ЭТО должно быть примерно так ...

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


    };

Так я и сделал ....

5 голосов
/ 03 октября 2009

Вы захотите использовать Entity Framework для настройки сопоставления «многие ко многим» от компании к продукту. При этом будет использоваться таблица 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
            };

Пожалуйста, используйте кнопку «Добавить комментарий», если вы хотите предоставить больше информации, вместо создания другого ответа.

2 голосов
/ 29 октября 2010

ЛЕВЫЕ НАРУЖНЫЕ СОЕДИНЕНИЯ выполняются с помощью GroupJoin в Entity Framework:

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

1 голос
/ 20 августа 2010

Обычное групповое объединение представляет собой левое внешнее объединение. Попробуйте это:

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.

0 голосов
/ 06 октября 2008

Как насчет этого (у вас есть много-много отношений между Компанией и Продуктом в вашем Entity Designer, не так ли?):

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

Entity Framework должен уметь определять тип используемого соединения.

0 голосов
/ 17 сентября 2008

Пожалуйста, попробуйте что-то вроде этого:

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;

Ура! * * 1004

...