Linq do Sql: Wiele lewych złączeń zewnętrznych

160

Mam problem ze zrozumieniem, jak używać więcej niż jednego lewego sprzężenia zewnętrznego przy użyciu LINQ to SQL. Rozumiem, jak używać jednego lewego sprzężenia zewnętrznego. Używam VB.NET. Poniżej znajduje się moja składnia SQL.

T-SQL

SELECT
    o.OrderNumber,
    v.VendorName,
    s.StatusName
FROM
    Orders o
LEFT OUTER JOIN Vendors v ON
    v.Id = o.VendorId
LEFT OUTER JOIN Status s ON
    s.Id = o.StatusId
WHERE
    o.OrderNumber >= 100000 AND
    o.OrderNumber <= 200000
Bryan Roth
źródło

Odpowiedzi:

247

To może być czystsze ( nie potrzebujesz wszystkich intoinstrukcji ):

var query = 
    from order in dc.Orders
    from vendor 
    in dc.Vendors
        .Where(v => v.Id == order.VendorId)
        .DefaultIfEmpty()
    from status 
    in dc.Status
        .Where(s => s.Id == order.StatusId)
        .DefaultIfEmpty()
    select new { Order = order, Vendor = vendor, Status = status } 
    //Vendor and Status properties will be null if the left join is null

Oto kolejny przykład łączenia po lewej stronie

var results = 
    from expense in expenseDataContext.ExpenseDtos
    where expense.Id == expenseId //some expense id that was passed in
    from category 
    // left join on categories table if exists
    in expenseDataContext.CategoryDtos
                         .Where(c => c.Id == expense.CategoryId)
                         .DefaultIfEmpty() 
    // left join on expense type table if exists
    from expenseType 
    in expenseDataContext.ExpenseTypeDtos
                         .Where(e => e.Id == expense.ExpenseTypeId)
                         .DefaultIfEmpty()
    // left join on currency table if exists
    from currency 
    in expenseDataContext.CurrencyDtos
                         .Where(c => c.CurrencyID == expense.FKCurrencyID)
                         .DefaultIfEmpty() 
    select new 
    { 
        Expense = expense,
        // category will be null if join doesn't exist
        Category = category,
        // expensetype will be null if join doesn't exist
        ExpenseType = expenseType,
        // currency will be null if join doesn't exist
        Currency = currency  
    }
Amir
źródło
12
@manitra: Nie, w rzeczywistości otrzymujesz instrukcje LEFT OUTER JOIN (bez zagnieżdżonych selekcji). Dość szalone, co?
Amir
6
Takie podejście podoba mi się bardziej niż używanie wszystkich instrukcji into. Dzięki za opublikowanie tego!
Bryan Roth,
7
To jest wszelkiego rodzaju słodycze. Jednak: wtf, dlaczego w linq nie ma lewej złączenia, jeśli istnieje złączenie? Jaki świat oparty na zbiorach łączy tylko wewnętrzne połączenia? Grrr.
jcollum
2
To tylko wywołało szeroki uśmiech na mojej twarzy. Dzięki za łatwy do naśladowania przykład.
nycdan
2
Próbowałem tego i było to o rząd wielkości wolniejsze niż metoda @ tvanfosson. Nie robiłem tego bezpośrednio w bazie danych, ale raczej ściśle w linq do obiektów. Miałem równowartość 500000 wydatków, 4000 categoryDtos i 4000 expenseTypeDtos. Uruchomienie zajęło 1 minutę. Ze składnią tvanfosson zajmuje to 6 sekund.
Chris,
49

Nie mam dostępu do VisualStudio (jestem na moim Macu), ale korzystam z informacji z http://bhaidar.net/cs/archive/2007/08/01/left-outer-join-in-linq-to -sql.aspx wygląda na to, że możesz zrobić coś takiego:

var query = from o in dc.Orders
            join v in dc.Vendors on o.VendorId equals v.Id into ov
            from x in ov.DefaultIfEmpty()
            join s in dc.Status on o.StatusId equals s.Id into os
            from y in os.DefaultIfEmpty()
            select new { o.OrderNumber, x.VendorName, y.StatusName }
tvanfosson
źródło
22

Dowiedziałem się, jak używać wielu lewych zewnętrznych sprzężeń w VB.NET za pomocą LINQ to SQL:

Dim db As New ContractDataContext()

Dim query = From o In db.Orders _
            Group Join v In db.Vendors _
            On v.VendorNumber Equals o.VendorNumber _
            Into ov = Group _
            From x In ov.DefaultIfEmpty() _
            Group Join s In db.Status _
            On s.Id Equals o.StatusId Into os = Group _
            From y In os.DefaultIfEmpty() _
            Where o.OrderNumber >= 100000 And o.OrderNumber <= 200000 _
            Select Vendor_Name = x.Name, _
                   Order_Number = o.OrderNumber, _
                   Status_Name = y.StatusName
Bryan Roth
źródło
8

W VB.NET przy użyciu funkcji,

Dim query = From order In dc.Orders
            From vendor In 
            dc.Vendors.Where(Function(v) v.Id = order.VendorId).DefaultIfEmpty()
            From status In 
            dc.Status.Where(Function(s) s.Id = order.StatusId).DefaultIfEmpty()
            Select Order = order, Vendor = vendor, Status = status 
Mitul
źródło
3

Myślę, że powinieneś być w stanie zastosować metodę zastosowaną w tym przypadku poście. Wygląda naprawdę brzydko, ale myślę, że możesz to zrobić dwa razy i uzyskać pożądany efekt.

Zastanawiam się, czy jest to rzeczywiście przypadek, w którym lepiej byłoby użyć DataContext.ExecuteCommand(...)zamiast konwertować na linq.

Jon Norton
źródło
0

Używam tego zapytania linq dla mojej aplikacji. jeśli to pasuje do twoich wymagań, możesz to polecić. tutaj dołączyłem (lewe połączenie zewnętrzne) z 3 tabelami.

 Dim result = (From csL In contractEntity.CSLogin.Where(Function(cs) cs.Login = login AndAlso cs.Password = password).DefaultIfEmpty
                   From usrT In contractEntity.UserType.Where(Function(uTyp) uTyp.UserTypeID = csL.UserTyp).DefaultIfEmpty ' <== makes join left join
                   From kunD In contractEntity.EmployeeMaster.Where(Function(kunDat) kunDat.CSLoginID = csL.CSLoginID).DefaultIfEmpty
                   Select New With {
                  .CSLoginID = csL.CSLoginID,
                  .UserType = csL.UserTyp}).ToList()
Iam ck
źródło