Linq a Sql: múltiples combinaciones externas izquierdas

160

Tengo algunos problemas para descubrir cómo usar más de una combinación externa izquierda usando LINQ to SQL. Entiendo cómo usar una combinación externa izquierda. Estoy usando VB.NET. A continuación se muestra mi sintaxis 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
fuente

Respuestas:

247

Esto puede ser más limpio ( no necesita todas las intodeclaraciones ):

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

Aquí hay otro ejemplo de combinación izquierda

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
fuente
12
@manitra: No, en realidad obtienes sentencias LEFT OUTER JOIN (sin selecciones anidadas). Bastante loco ¿eh?
Amir
66
Me gusta más este enfoque que usar todas las declaraciones into. ¡Gracias por publicar esto!
Bryan Roth
77
Esto es todo tipo de dulce. Sin embargo: ¿por qué no hay una unión izquierda en linq si hay una unión? ¿Qué mundo basado en conjuntos solo une internamente? Grrr.
jcollum
2
Esto solo puso una gran sonrisa en mi cara. Gracias por el ejemplo fácil de seguir.
nycdan
2
Intenté esto y fue un orden de magnitud más lento que el método de @ tvanfosson. No lo estaba haciendo directamente contra una base de datos, sino estrictamente en linq a los objetos. Tenía el equivalente de 500000 gastos, 4000 categoryDtos y 4000 gastoTypeDtos. Tardó 1 minuto en correr. Con la sintaxis de tvanfosson, lleva 6 segundos.
Chris
49

No tengo acceso a VisualStudio (estoy en mi Mac), pero utilizo la información de http://bhaidar.net/cs/archive/2007/08/01/left-outer-join-in-linq-to -sql.aspx parece que puede hacer algo como esto:

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
fuente
22

Descubrí cómo usar múltiples combinaciones externas izquierdas en VB.NET usando 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
fuente
8

En VB.NET usando 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
fuente
3

Creo que deberías poder seguir el método utilizado en este publicación. Se ve muy feo, pero creo que podría hacerlo dos veces y obtener el resultado que desea.

Me pregunto si este es realmente un caso en el que sería mejor usarlo en DataContext.ExecuteCommand(...)lugar de convertirlo a linq.

Jon Norton
fuente
0

Estoy usando esta consulta linq para mi aplicación. si esto coincide con su requerimiento, puede referirlo. aquí me he unido (combinación externa izquierda) con 3 tablas.

 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
fuente