Linq vers Sql: plusieurs jointures externes à gauche

160

J'ai du mal à comprendre comment utiliser plus d'une jointure externe gauche à l'aide de LINQ to SQL. Je comprends comment utiliser une jointure externe gauche. J'utilise VB.NET. Voici ma syntaxe 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
la source

Réponses:

247

Cela peut être plus propre ( vous n'avez pas besoin de toutes les intodéclarations ):

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

Voici un autre exemple de jointure à gauche

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
la source
12
@manitra: Non, vous obtenez en fait des instructions LEFT OUTER JOIN (pas de sélections imbriquées). Assez fou hein?
Amir
6
J'aime mieux cette approche que d'utiliser toutes les instructions into. Merci d'avoir publié ceci!
Bryan Roth
7
C'est toutes sortes de sucreries. Cependant: pourquoi n'y a-t-il pas de jointure gauche dans linq s'il y a une jointure? Quel monde basé sur des ensembles ne fait que des jointures internes? Grrr.
jcollum
2
Cela m'a juste fait un grand sourire. Merci pour l'exemple facile à suivre.
nycdan
2
J'ai essayé ceci et c'était un ordre de grandeur plus lent que la méthode de @ tvanfosson. Je ne le faisais pas directement contre une base de données, mais plutôt strictement dans linq aux objets. J'avais l'équivalent de 500000 dépenses, 4000 categoryDtos et 4000 depenseTypeDtos. Il a fallu 1 minute pour courir. Avec la syntaxe de tvanfosson, cela prend 6 secondes.
Chris
49

Je n'ai pas accès à VisualStudio (je suis sur mon Mac), mais en utilisant les informations de http://bhaidar.net/cs/archive/2007/08/01/left-outer-join-in-linq-to -sql.aspx il semble que vous puissiez faire quelque chose comme ceci:

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
la source
22

J'ai compris comment utiliser plusieurs jointures externes gauches dans VB.NET en utilisant 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
la source
8

Dans VB.NET en utilisant 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
la source
3

Je pense que vous devriez pouvoir suivre la méthode utilisée dans cet article. Cela a l'air vraiment moche, mais je pense que vous pouvez le faire deux fois et obtenir le résultat souhaité.

Je me demande si c'est réellement un cas où vous feriez mieux d'utiliser DataContext.ExecuteCommand(...)au lieu de convertir en linq.

Jon Norton
la source
0

J'utilise cette requête linq pour mon application. si cela correspond à vos besoins, vous pouvez le référer. ici, j'ai joint (jointure externe gauche) avec 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()
Je suis ck
la source