161

I'm having some trouble figuring out how to use more than one left outer join using LINQ to SQL. I understand how to use one left outer join. I'm using VB.NET. Below is my SQL syntax.

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
mwigdahl
  • 16,268
  • 7
  • 50
  • 64
Bryan Roth
  • 10,479
  • 15
  • 47
  • 56

6 Answers6

250

This may be cleaner (you dont need all the into statements):

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

Here is another left join example

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
  • 9,091
  • 5
  • 34
  • 46
  • 1
    How does the generated SQL look like ? Doesn't it contains nested select this way ? – Manitra Andriamitondra Feb 15 '10 at 09:55
  • 12
    @manitra: No, you actually get LEFT OUTER JOIN statements (no nested selects). Pretty crazy huh? – Amir Feb 15 '10 at 20:23
  • 6
    I like this approach better than using all the into statements. Thanks for posting this! – Bryan Roth Mar 22 '10 at 21:57
  • 8
    This is all kinds of sweet. However: wtf why isn't there a left join in linq if there's a join? What set-based world only does inner joins? Grrr. – jcollum Nov 09 '10 at 17:56
  • This worked excellent to get Linq to generate nice clean queries with left joins in them, thanks! – Sgraffite Jan 17 '11 at 05:34
  • 2
    This just put a big smile on my face. Thanks for the easy-to-follow example. – nycdan Jan 28 '11 at 21:49
  • It is a good solution but in some cases (like when you have imported Table-Value Functions) you may encounter some exceptions which say it cannot translate your method to SQL – Mahmoud Moravej Jun 11 '13 at 13:23
  • 2
    I tried this and it was an order of magnitude slower than @tvanfosson's method. I was not doing it directly against a database, but rather strictly in linq to objects. I had the equivalent of 500000 expenses, 4000 categoryDtos, and 4000 expenseTypeDtos. It took 1 minute to run. With tvanfosson's syntax it takes 6 seconds. – Christopher Feb 11 '16 at 22:12
  • Why is the accepted answer to a VB.Net question written in C#.Net syntax.? – JDC Aug 20 '19 at 08:08
49

Don't have access to VisualStudio (I'm on my Mac), but using the information from http://bhaidar.net/cs/archive/2007/08/01/left-outer-join-in-linq-to-sql.aspx it looks like you may be able to do something like this:

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
  • 524,688
  • 99
  • 697
  • 795
22

I figured out how to use multiple left outer joins in VB.NET using 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
Rowland Shaw
  • 37,700
  • 14
  • 97
  • 166
Bryan Roth
  • 10,479
  • 15
  • 47
  • 56
8

In VB.NET using Function,

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
  • 9,734
  • 4
  • 43
  • 60
3

I think you should be able to follow the method used in this post. It looks really ugly, but I would think you could do it twice and get the result you want.

I wonder if this is actually a case where you'd be better off using DataContext.ExecuteCommand(...) instead of converting to linq.

Jon Norton
  • 2,969
  • 21
  • 20
0

I am using this linq query for my application. if this match your requirement you can refer this. here i have joined(Left outer join) with 3 tables.

 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
  • 1
  • 3