55

Sto provando LINQ alle entità.LEFT JOIN in LINQ alle entità?

Ho un problema con il seguente: io voglio fare questo:

SELECT 
    T_Benutzer.BE_User 
    ,T_Benutzer_Benutzergruppen.BEBG_BE 
FROM T_Benutzer 

LEFT JOIN T_Benutzer_Benutzergruppen 
    ON T_Benutzer_Benutzergruppen.BEBG_BE = T_Benutzer.BE_ID 

la cosa più vicina che ho incontra è questo:

 var lol = (
      from u in Repo.T_Benutzer 

      //where u.BE_ID == 1 
      from o in Repo.T_Benutzer_Benutzergruppen.DefaultIfEmpty() 
       // on u.BE_ID equals o.BEBG_BE 

      where (u.BE_ID == o.BEBG_BE || o.BEBG_BE == null) 

      //join bg in Repo.T_Benutzergruppen.DefaultIfEmpty() 
      // on o.BEBG_BG equals bg.ID 

      //where bg.ID == 899 

      orderby 
       u.BE_Name ascending 
       //, bg.Name descending 

      //select u 
      select new 
      { 
       u.BE_User 
       ,o.BEBG_BG 
       //, bg.Name 
      } 
     ).ToList(); 

Ma questo genera lo stesso risulta come un join interno e non un join sinistro.
Inoltre, crea questa SQL completamente pazzo:

SELECT 
    [Extent1].[BE_ID] AS [BE_ID] 
    ,[Extent1].[BE_User] AS [BE_User] 
    ,[Join1].[BEBG_BG] AS [BEBG_BG] 
FROM [dbo].[T_Benutzer] AS [Extent1] 

CROSS JOIN 
(
    SELECT 
     [Extent2].[BEBG_BE] AS [BEBG_BE] 
     ,[Extent2].[BEBG_BG] AS [BEBG_BG] 
    FROM (SELECT 1 AS X) AS [SingleRowTable1] 
    LEFT OUTER JOIN [dbo].[T_Benutzer_Benutzergruppen] AS [Extent2] 
     ON 1 = 1 
) AS [Join1] 

WHERE [Extent1].[BE_ID] = [Join1].[BEBG_BE] 
OR [Join1].[BEBG_BE] IS NULL 

ORDER BY [Extent1].[BE_Name] ASC 

Come posso fare un LEFT JOIN in LINQ-2-entità in un modo in cui un'altra persona può ancora capire che cosa si sta facendo in quel codice?

e la maggior parte, preferibilmente in cui l'SQL generato assomiglia:

SELECT 
    T_Benutzer.BE_User 
    ,T_Benutzer_Benutzergruppen.BEBG_BE 
FROM T_Benutzer 

LEFT JOIN T_Benutzer_Benutzergruppen 
    ON T_Benutzer_Benutzergruppen.BEBG_BE = T_Benutzer.BE_ID 
+0

Duplicate - http://stackoverflow.com/questions/3404975/left-outer-join-in-linq – Anand

+1

@Anand: No, unire cede sempre join interno, e da senza una condizione è un cross-join, la la risposta selezionata è, nonostante le sue numerose revisioni, errata e insufficiente. –

risposta

103

Ah, ho capito.
I quirk e quark delle entità LINQ-2.
Questo sembra più comprensibile:

var query2 = (
    from users in Repo.T_Benutzer 
    from mappings in Repo.T_Benutzer_Benutzergruppen 
     .Where(mapping => mapping.BEBG_BE == users.BE_ID).DefaultIfEmpty() 
    from groups in Repo.T_Benutzergruppen 
     .Where(gruppe => gruppe.ID == mappings.BEBG_BG).DefaultIfEmpty() 
    //where users.BE_Name.Contains(keyword) 
    // //|| mappings.BEBG_BE.Equals(666) 
    //|| mappings.BEBG_BE == 666 
    //|| groups.Name.Contains(keyword) 

    select new 
    { 
     UserId = users.BE_ID 
     ,UserName = users.BE_User 
     ,UserGroupId = mappings.BEBG_BG 
     ,GroupName = groups.Name 
    } 

); 


var xy = (query2).ToList(); 

Rimuovere il .DefaultIfEmpty(), e si ottiene un join interno.
Questo era quello che stavo cercando.

+5

Così tanti anni si uniscono nell'altro modo! Grazie! – Todd

+1

Ottimo! La prima volta che vedo una cosa del genere. Grazie – teapeng

34

È possibile leggere un articolo che ho scritto per unisce in LINQ here

var query = 
from u in Repo.T_Benutzer 
join bg in Repo.T_Benutzer_Benutzergruppen 
    on u.BE_ID equals bg.BEBG_BE 
into temp 
from j in temp.DefaultIfEmpty() 
select new 
{ 
    BE_User = u.BE_User, 
    BEBG_BG = (int?)j.BEBG_BG// == null ? -1 : j.BEBG_BG 
      //, bg.Name 
} 

Il seguente è l'equivalente metodi di estensione che utilizzano :

var query = 
Repo.T_Benutzer 
.GroupJoin 
(
    Repo.T_Benutzer_Benutzergruppen, 
    x=>x.BE_ID, 
    x=>x.BEBG_BE, 
    (o,i)=>new {o,i} 
) 
.SelectMany 
(
    x => x.i.DefaultIfEmpty(), 
    (o,i) => new 
    { 
     BE_User = o.o.BE_User, 
     BEBG_BG = (int?)i.BEBG_BG 
    } 
); 
+0

È T_Benutzer_Benutzergruppen, non T_Benutzergruppen, ma altrimenti corretto. Basta chiedersi come dovrebbe funzionare quando si è unito a più di due tavoli. Stavo cercando un metodo più intuitivamente comprensibile. E alla fine l'ho trovato :) –

+0

Personalmente sono abituato ai metodi di estensione uno e mi piace molto. Se continui a ripetere le coppie 'GroupJoin' e' SelectMany' puoi avere una soluzione carina, anche se lunga :) –

1

È possibile utilizzare questo non solo in entità, ma anche procedura di memorizzazione o di altra origine dati:

var customer = (from cus in _billingCommonservice.BillingUnit.CustomerRepository.GetAll() 
          join man in _billingCommonservice.BillingUnit.FunctionRepository.ManagersCustomerValue() 
          on cus.CustomerID equals man.CustomerID 
          // start left join 
          into a 
          from b in a.DefaultIfEmpty(new DJBL_uspGetAllManagerCustomer_Result()) 
          select new { cus.MobileNo1,b.ActiveStatus }); 
+0

Errore durante la simulazione di query con DefaultIfEmpty. Ottenuto da qui l'idea di creare una classe predefinita 'a.DefaultIfEmpty (new DJBL_uspGetAllManagerCustomer_Result()) 'e ha funzionato! – Riga

+0

Sfortunatamente la creazione dell'entità non ha esito positivo al test di integrazione, quindi non è una buona soluzione. – Riga

5

Può essere vengo più tardi per rispondere, ma in questo momento sto affrontando con questo ... se non ci aiuta sono un'altra soluzione (il modo in cui l'ho risolto).

var query2 = (
    from users in Repo.T_Benutzer 
    join mappings in Repo.T_Benutzer_Benutzergruppen on mappings.BEBG_BE equals users.BE_ID into tmpMapp 
    join groups in Repo.T_Benutzergruppen on groups.ID equals mappings.BEBG_BG into tmpGroups 
    from mappings in tmpMapp.DefaultIfEmpty() 
    from groups in tmpGroups.DefaultIfEmpty() 
    select new 
    { 
     UserId = users.BE_ID 
     ,UserName = users.BE_User 
     ,UserGroupId = mappings.BEBG_BG 
     ,GroupName = groups.Name 
    } 

); 

A proposito, ho provato ad utilizzare il codice di Stefan Steiger che aiuta anche ma era lento come l'inferno.

+1

Per caso lo stai facendo in Linq-2-Objects? Perché lì sarà lento, perché non usa un indice. –