¿Cómo se realiza una unión externa izquierda usando métodos de extensión de linq?

Resuelto LaserJesus asked hace 15 años • 10 respuestas

Suponiendo que tengo una unión externa izquierda como tal:

from f in Foo
join b in Bar on f.Foo_Id equals b.Foo_Id into g
from result in g.DefaultIfEmpty()
select new { Foo = f, Bar = result }

¿Cómo expresaría la misma tarea usando métodos de extensión? P.ej

Foo.GroupJoin(Bar, f => f.Foo_Id, b => b.Foo_Id, (f,b) => ???)
    .Select(???)
LaserJesus avatar Feb 25 '09 12:02 LaserJesus
Aceptado

Para una unión (exterior izquierda) de una tabla Barcon una tabla Fooen Foo.Foo_Id = Bar.Foo_Idnotación lambda:

var qry = Foo.GroupJoin(
          Bar, 
          foo => foo.Foo_Id,
          bar => bar.Foo_Id,
          (x,y) => new { Foo = x, Bars = y })
       .SelectMany(
           x => x.Bars.DefaultIfEmpty(),
           (x,y) => new { Foo=x.Foo, Bar=y});
Marc Gravell avatar Feb 25 '2009 05:02 Marc Gravell

Dado que esta parece ser la pregunta SO de facto para uniones externas izquierdas usando la sintaxis del método (extensión), pensé en agregar una alternativa a la respuesta seleccionada actualmente que (al menos en mi experiencia) ha sido más comúnmente lo que estoy después

// Option 1: Expecting either 0 or 1 matches from the "Right"
// table (Bars in this case):
var qry = Foos.GroupJoin(
          Bars,
          foo => foo.Foo_Id,
          bar => bar.Foo_Id,
          (f,bs) => new { Foo = f, Bar = bs.SingleOrDefault() });

// Option 2: Expecting either 0 or more matches from the "Right" table
// (courtesy of currently selected answer):
var qry = Foos.GroupJoin(
                  Bars, 
                  foo => foo.Foo_Id,
                  bar => bar.Foo_Id,
                  (f,bs) => new { Foo = f, Bars = bs })
              .SelectMany(
                  fooBars => fooBars.Bars.DefaultIfEmpty(),
                  (x,y) => new { Foo = x.Foo, Bar = y });

Para mostrar la diferencia usando un conjunto de datos simple (suponiendo que unimos los valores mismos):

List<int> tableA = new List<int> { 1, 2, 3 };
List<int?> tableB = new List<int?> { 3, 4, 5 };

// Result using both Option 1 and 2. Option 1 would be a better choice
// if we didn't expect multiple matches in tableB.
{ A = 1, B = null }
{ A = 2, B = null }
{ A = 3, B = 3    }

List<int> tableA = new List<int> { 1, 2, 3 };
List<int?> tableB = new List<int?> { 3, 3, 4 };

// Result using Option 1 would be that an exception gets thrown on
// SingleOrDefault(), but if we use FirstOrDefault() instead to illustrate:
{ A = 1, B = null }
{ A = 2, B = null }
{ A = 3, B = 3    } // Misleading, we had multiple matches.
                    // Which 3 should get selected (not arbitrarily the first)?.

// Result using Option 2:
{ A = 1, B = null }
{ A = 2, B = null }
{ A = 3, B = 3    }
{ A = 3, B = 3    }    

La opción 2 es fiel a la definición típica de unión externa izquierda, pero, como mencioné anteriormente, a menudo es innecesariamente compleja según el conjunto de datos.

Ocelot20 avatar Jun 17 '2014 21:06 Ocelot20