2017-03-15 3 views
1

Dies ist die SQL-Abfrage zu holen ich in einer LINQ-Abfrage zu konvertieren Ich versuche:Nicht immer LEFT Abfrage in LINQ JOIN leere Zeilen

SELECT 
    ContactID, 
    COUNT (ls.SoldToContactID) AS Count 
FROM 
    Contacts c 
    LEFT OUTER JOIN LeadSales ls on c.ContactID = ls.SoldToContactID 
GROUP BY 
    c.ContactID 

ich die Ergebnisse von leadSales erwartet bekommen, aber ich bin Keine der Zeilen von contacts, die keine Übereinstimmung in leadSales haben.

var leadSales = (IQueryable of the LeadSales table) 
var contacts = (IQueryable of the Contacts table) 

BuyerList = from ls in leadSales 
    join contact in contacts on ls.SoldToContactID equals contact.ContactID into sb 
    from subBuyer in sb.DefaultIfEmpty() 
     group ls by new { 
      subBuyer.ContactID, 
      FirstName = subBuyer.FirstName, 
      LastName = subBuyer.LastName 
     } into g 
     select new LeadBuyersByStateItem 
     { 
      ContactID = g.Key.ContactID, 
      Name = g.Key.LastName + ", " + g.Key.FirstName, 
      LeadsCount = g.Count() 
     }; 

EDIT:

Basierend auf dem Feedback von Ivan, ich das jetzt haben:

BuyerList = from contact in contacts 
         join ls in leadSales on contact.ContactID equals ls.SoldToContactID into c_ls 
         from ls in c_ls.DefaultIfEmpty() 
         group contact by new 
         { 
          ls.Contact.ContactID, 
          FirstName = ls.Contact.FirstName, 
          LastName = ls.Contact.LastName 
         } into g 
         select new LeadBuyersByStateItem 
         { 
          ContactID = g.Key.ContactID, 
          Name = g.Key.LastName + ", " + g.Key.FirstName, 
          LeadsCount = g.Count() 
         }; 

Aber das diesen Fehler zu werfen:

The cast to value type 'System.Int32' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.

+3

Sie geben nicht auf :) Ich könnte es zum dritten Mal schließen, weil es immer noch ein Duplikat von http://stackoverflow.com/questions/3404975/left-outer-join-in-linq ist. Stattdessen gebe ich Ihnen diesmal einen Hinweis: 'von c in Kontakte verbinden ls in leadSales auf c.ContactID gleich ls.SoldToContactID in c_ls von ls in c_ls.DefaultIfEmpty() ...'. Hoffe du kannst es von dort aus schaffen. –

+0

Mögliches Duplikat von [LEFT OUTER JOIN in LINQ] (http: // stackoverflow.com/questions/3404975/left-outer-join-in-linq) – mrfreester

+0

@Ivan, das hatte ich schon nach dem letzten Kommentar (beim zweiten Mal) ausprobiert. Schrauben Sie es. Ich werde nur das SQL verwenden und LINQ vergessen. –

Antwort

3

Das Problem mit der anfänglichen LINQ-Abfrage ist, dass Sie das linke und rechte p ausgetauscht haben Kunst, so war es äquivalent zu SQL LeadSales LEFT OUTER JOIN Contacts.

Das Problem mit der aktualisierten Abfrage ist, dass die group by Klausel ls.Contact verwendet, und da ls ist das rechte Ende des linken äußeren Verknüpfung, kann es null sein. Verwenden Sie die contact Variable (das linke Ende des Joins, das garantiert vorhanden ist), um die SQL-Abfrage abzugleichen. Beachten Sie auch, dass SQL COUNT(ls.SoldToContactID) Werte NULL ausschließt, so dass Sie dies in der LINQ-Abfrage berücksichtigen müssen (es gibt kein direktes LINQ-äquivalentes Konstrukt).

So ist die direkte LINQ Übersetzung der SQL-Abfrage ist wie folgt:

from c in contacts 
join ls in leadSales on c.ContactID equals ls.SoldToContactID into c_ls 
from ls in c_ls.DefaultIfEmpty() 
group ls by new 
{ 
    c.ContactID, 
    c.FirstName, 
    c.LastName 
} into g 
select new LeadBuyersByStateItem 
{ 
    ContactID = g.Key.ContactID, 
    Name = g.Key.LastName + ", " + g.Key.FirstName, 
    LeadsCount = g.Count(ls => ls != null) 
}; 

Aber wenn Sie die SQL-Abfrage vergessen und auf das gewünschte Ergebnis konzentrieren, werden Sie sehen, dass es einfacher LINQ/EF-Versionen der gleichen Abfrage.

Am einfachsten ist es, wenn Sie Navigationseigenschaft von Contact zu LeadSale (Ich habe bemerkt, dass Sie von LeadSale zu Contact haben) haben wie

public ICollection<LeadSale> LeadSales { get; set; } 

Dann wird die Abfrage ist einfach:

from c in contacts 
select new LeadBuyersByStateItem 
{ 
    ContactID = c.ContactID, 
    Name = c.LastName + ", " + c.FirstName, 
    LeadsCount = c.LeadSales.Count() 
}; 

Wenn Sie haben keine solche Eigenschaft, können Sie Count mit Bedingung verwenden:

from c in contacts 
select new LeadBuyersByStateItem 
{ 
    ContactID = c.ContactID, 
    Name = c.LastName + ", " + c.FirstName, 
    LeadsCount = leadSales.Count(ls => ls.SoldToContactID == c.ContactID) 
}; 

oder group join:

from c in contacts 
join ls in leadSales on c.ContactID equals ls.SoldToContactID into c_ls 
select new LeadBuyersByStateItem 
{ 
    ContactID = c.ContactID, 
    Name = c.LastName + ", " + c.FirstName, 
    LeadsCount = c_ls.Count() 
}; 

Zur Erinnerung, in allen Fällen sollten Sie die LINQ-Abfrage mit der erforderlichen Tabelle starten.