IZQUIERDA EXTERIOR ÚNETE en LINQ

539

¿Cómo realizar la unión externa izquierda en C # LINQ a objetos sin usar join-on-equals-intocláusulas? ¿Hay alguna manera de hacer eso con wherecláusula? Problema correcto: la unión interna es fácil y tengo una solución como esta

List<JoinPair> innerFinal = (from l in lefts from r in rights where l.Key == r.Key
                             select new JoinPair { LeftId = l.Id, RightId = r.Id})

pero para la unión externa izquierda necesito una solución. El mío es algo como esto pero no funciona

List< JoinPair> leftFinal = (from l in lefts from r in rights
                             select new JoinPair { 
                                            LeftId = l.Id, 
                                            RightId = ((l.Key==r.Key) ? r.Id : 0
                                        })

donde JoinPair es una clase:

public class JoinPair { long leftId; long rightId; }
Juguete
fuente
2
¿Puedes dar un ejemplo de lo que estás tratando de lograr?
jeroenh
la unión externa izquierda normal es algo como esto: var a = de b en bb se une a c en cc en b.bbbbb es igual a c.ccccc en dd desde d en dd.DefaultIfEmpty () seleccione b.sss; Mi pregunta es si hay alguna manera de hacerlo sin usar cláusulas join-on-equals-into algo así como var a = desde b en bb desde c en cc donde b.bbb == c.cccc ... y así sucesivamente. .
Juguete el
1
seguro que lo hay, pero debe publicar un ejemplo de su código que ya tiene para que la gente pueda darle una mejor respuesta
perezoso el
Estaba buscando una "Izquierda excluyendo " JOIN (y lo confundí con el concepto de "EXTERIOR"). Esta respuesta estaba más cerca de lo que quería.
The Red Pea

Respuestas:

598

Como se indica en:

101 muestras LINQ - Unión externa izquierda

var q =
    from c in categories
    join p in products on c.Category equals p.Category into ps
    from p in ps.DefaultIfEmpty()
    select new { Category = c, ProductName = p == null ? "(No products)" : p.ProductName };
ajay_whiz
fuente
77
Estoy intentando lo mismo, pero obtengo un error en el operador de combinación, que dice "El tipo de una de las expresiones en la cláusula de combinación es incorrecto".
Badhon Jain
3
@jain si sus tipos son diferentes, la unión no funcionará. Es muy probable que sus claves sean de diferentes tipos de datos. ¿Son ambas claves int por ejemplo?
Yooakim
2
¿Cuál es la solución Jain? También estoy enfrentando el mismo error y los tipos son los mismos en mi caso también.
Sandeep
1
@Sandeep revisa tus llaves donde te uniste. Supongamos que si son de tipo string e int, entonces simplemente convierta la clave de cadena a int.
Ankit
2
enlace actualizado: 101 muestras LINQ
Unión
546

Si se utiliza un proveedor LINQ basado en una base de datos, se puede escribir una combinación externa izquierda significativamente más legible como tal:

from maintable in Repo.T_Whatever 
from xxx in Repo.T_ANY_TABLE.Where(join condition).DefaultIfEmpty()

Si omites el DefaultIfEmpty() , tendrá una unión interna.

Tome la respuesta aceptada:

  from c in categories
    join p in products on c equals p.Category into ps
    from p in ps.DefaultIfEmpty()

Esta sintaxis es muy confusa, y no está claro cómo funciona cuando quieres unir tablas MÚLTIPLES.

Nota:
Debe tenerse en cuenta que from alias in Repo.whatever.Where(condition).DefaultIfEmpty()es lo mismo que una aplicación externa / combinación izquierda-lateral, que cualquier optimizador de base de datos (decente) es perfectamente capaz de traducir en una combinación izquierda, siempre que no introduzca filas por fila. -valores (también conocido como una aplicación externa real). No haga esto en Linq-2-Objects (porque no hay un optimizador de DB cuando usa Linq-to-Objects).

Ejemplo detallado

var query2 = (
    from users in Repo.T_User
    from mappings in Repo.T_User_Group
         .Where(mapping => mapping.USRGRP_USR == users.USR_ID)
         .DefaultIfEmpty() // <== makes join left join
    from groups in Repo.T_Group
         .Where(gruppe => gruppe.GRP_ID == mappings.USRGRP_GRP)
         .DefaultIfEmpty() // <== makes join left join

    // where users.USR_Name.Contains(keyword)
    // || mappings.USRGRP_USR.Equals(666)  
    // || mappings.USRGRP_USR == 666 
    // || groups.Name.Contains(keyword)

    select new
    {
         UserId = users.USR_ID
        ,UserName = users.USR_User
        ,UserGroupId = groups.ID
        ,GroupName = groups.Name
    }

);


var xy = (query2).ToList();

Cuando se usa con LINQ 2 SQL, se traducirá muy bien a la siguiente consulta SQL muy legible:

SELECT 
     users.USR_ID AS UserId 
    ,users.USR_User AS UserName 
    ,groups.ID AS UserGroupId 
    ,groups.Name AS GroupName 
FROM T_User AS users

LEFT JOIN T_User_Group AS mappings
   ON mappings.USRGRP_USR = users.USR_ID

LEFT JOIN T_Group AS groups
    ON groups.GRP_ID == mappings.USRGRP_GRP

Editar:

Consulte también " Convertir consulta SQL Server a consulta Linq " para obtener un ejemplo más complejo.

Además, si lo está haciendo en Linq-2-Objects (en lugar de Linq-2-SQL), debe hacerlo a la antigua usanza (porque LINQ to SQL traduce esto correctamente para unir operaciones, pero sobre objetos este método fuerza un escaneo completo y no aprovecha las búsquedas de índice, por qué ...):

    var query2 = (
    from users in Repo.T_Benutzer
    join mappings in Repo.T_Benutzer_Benutzergruppen on mappings.BEBG_BE equals users.BE_ID into tmpMapp
    join groups in Repo.T_Benutzergruppen on groups.ID equals mappings.BEBG_BG into tmpGroups
    from mappings in tmpMapp.DefaultIfEmpty()
    from groups in tmpGroups.DefaultIfEmpty()
    select new
    {
         UserId = users.BE_ID
        ,UserName = users.BE_User
        ,UserGroupId = mappings.BEBG_BG
        ,GroupName = groups.Name
    }

);
Stefan Steiger
fuente
21
Esta respuesta es realmente útil. Gracias por ofrecer una sintaxis realmente comprensible.
Chris Marisic
3
WTB una consulta LINQ compatible con NHibernate ... :)
mxmissile
30
LINQ to SQL traduce esto correctamente para unir operaciones. Sin embargo, sobre los objetos, este método fuerza un escaneo completo. Esta es la razón por la cual la documentación oficial ofrece la solución de unión de grupo que puede aprovechar los hash para indexar búsquedas.
Tamir Daniely
3
Creo que la sintaxis de explícito joines mucho más legible y clara que la whereseguida porDefaultIfEmpty
FindOut_Quran
1
@ user3441905: siempre y cuando solo tenga que unir la tabla a con la tabla b, esto puede ser. Pero tan pronto como tenga más que eso, no lo será. Pero incluso para solo 2 mesas, creo que es demasiado verboso. La opinión popular también parece estar en su contra, ya que esta respuesta comenzó con 0 cuando la respuesta principal ya tenía más de 90 votos a favor.
Stefan Steiger
132

Usando la expresión lambda

db.Categories    
  .GroupJoin(db.Products,
      Category => Category.CategoryId,
      Product => Product.CategoryId,
      (x, y) => new { Category = x, Products = y })
  .SelectMany(
      xy => xy.Products.DefaultIfEmpty(),
      (x, y) => new { Category = x.Category, Product = y })
  .Select(s => new
  {
      CategoryName = s.Category.Name,     
      ProductName = s.Product.Name   
  });
Mecedora
fuente
8
Tanto Join como GroupJoin no son realmente compatibles con left-join. El truco con el uso de GroupJoin es que puede tener grupos vacíos y luego traducir esos grupos vacíos a valores vacíos. DefaultIfEmpty simplemente hace eso, lo Enumerable.Empty<Product>.DefaultIfEmpty()que significa que devolverá un IEnumerable con un solo valor de default(Product).
Tamir Daniely
61
Todo esto para realizar una unión izquierda?
FindOut_Quran
77
¡Gracias por esto! No hay muchos ejemplos de expresiones lambda por ahí, esto funcionó para mí.
Johan Henkens
1
Gracias por la respuesta. Se produjo lo más parecido a la prima de SQL LEFT OUTER JOIN que he escrito en los últimos años
John Gathogo
1
Realmente no necesito el último Select (), el anon obj en SelectMany () se puede refactorizar para la misma salida. Otro pensamiento es probar y para nulo para simular una equivalencia más cercana a la IZQUIERDA.
Denny Jacob
46

Ahora como método de extensión:

public static class LinqExt
{
    public static IEnumerable<TResult> LeftOuterJoin<TLeft, TRight, TKey, TResult>(this IEnumerable<TLeft> left, IEnumerable<TRight> right, Func<TLeft, TKey> leftKey, Func<TRight, TKey> rightKey,
        Func<TLeft, TRight, TResult> result)
    {
        return left.GroupJoin(right, leftKey, rightKey, (l, r) => new { l, r })
             .SelectMany(
                 o => o.r.DefaultIfEmpty(),
                 (l, r) => new { lft= l.l, rght = r })
             .Select(o => result.Invoke(o.lft, o.rght));
    }
}

Use como normalmente usaría join:

var contents = list.LeftOuterJoin(list2, 
             l => l.country, 
             r => r.name,
            (l, r) => new { count = l.Count(), l.country, l.reason, r.people })

Espero que esto te ahorre algo de tiempo.

Matas Vaitkevicius
fuente
44

Echa un vistazo a este ejemplo . Esta consulta debería funcionar:

var leftFinal = from left in lefts
                join right in rights on left equals right.Left into leftRights
                from leftRight in leftRights.DefaultIfEmpty()
                select new { LeftId = left.Id, RightId = left.Key==leftRight.Key ? leftRight.Id : 0 };
Devastar
fuente
3
¿Se rpuede acceder en la cláusula select después de usar un join into?
Farhad Alizadeh Noori
@FarhadAlizadehNoori Sí, puede.
Po-ta-toe
El autor probablemente tenía la intención de reutilizar ren la segunda fromcláusula. es decir, from r in lrs.DefaultIfEmpty()esta consulta no tiene mucho sentido y probablemente ni siquiera se compila debido a que restá fuera de contexto para la selección.
Saeb Amini
@Devart, cuando leí tu consulta me recordó a la película Clockwisecon John Cleese, jajaja.
Matas Vaitkevicius
1
De izquierda a derecha hacia la izquierda en derechos en izquierda en izquierdaRights ... Oh, Dios ... La sintaxis de usar LEFT OUTER JOIN en LINQ realmente no está clara, pero estos nombres realmente lo hacen aún más claro.
Mike Gledhill
19

Una implementación de combinación externa izquierda por métodos de extensión podría verse como

public static IEnumerable<Result> LeftJoin<TOuter, TInner, TKey, Result>(
  this IEnumerable<TOuter> outer, IEnumerable<TInner> inner
  , Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector
  , Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer)
  {
    if (outer == null)
      throw new ArgumentException("outer");

    if (inner == null)
      throw new ArgumentException("inner");

    if (outerKeySelector == null)
      throw new ArgumentException("outerKeySelector");

    if (innerKeySelector == null)
      throw new ArgumentException("innerKeySelector");

    if (resultSelector == null)
      throw new ArgumentException("resultSelector");

    return LeftJoinImpl(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer ?? EqualityComparer<TKey>.Default);
  }

  static IEnumerable<Result> LeftJoinImpl<TOuter, TInner, TKey, Result>(
      IEnumerable<TOuter> outer, IEnumerable<TInner> inner
      , Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector
      , Func<TOuter, TInner, Result> resultSelector, IEqualityComparer<TKey> comparer)
  {
    var innerLookup = inner.ToLookup(innerKeySelector, comparer);

    foreach (var outerElment in outer)
    {
      var outerKey = outerKeySelector(outerElment);
      var innerElements = innerLookup[outerKey];

      if (innerElements.Any())
        foreach (var innerElement in innerElements)
          yield return resultSelector(outerElment, innerElement);
      else
        yield return resultSelector(outerElment, default(TInner));
     }
   }

El selector de resultados debe ocuparse de los elementos nulos. Fx.

   static void Main(string[] args)
   {
     var inner = new[] { Tuple.Create(1, "1"), Tuple.Create(2, "2"), Tuple.Create(3, "3") };
     var outer = new[] { Tuple.Create(1, "11"), Tuple.Create(2, "22") };

     var res = outer.LeftJoin(inner, item => item.Item1, item => item.Item1, (it1, it2) =>
     new { Key = it1.Item1, V1 = it1.Item2, V2 = it2 != null ? it2.Item2 : default(string) });

     foreach (var item in res)
       Console.WriteLine(string.Format("{0}, {1}, {2}", item.Key, item.V1, item.V2));
   }
Bertrand
fuente
44
Sin embargo, esta es solo una opción para LINQ a objetos, y no podrá traducir la consulta a ningún proveedor de consultas, que es el caso de uso más común para esta operación.
Servicio
13
Pero la pregunta era "Cómo realizar la unión externa izquierda en C # LINQ a los objetos ..."
Bertrand
12

mira este ejemplo

class Person
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Phone { get; set; }
}

class Pet
{
    public string Name { get; set; }
    public Person Owner { get; set; }
}

public static void LeftOuterJoinExample()
{
    Person magnus = new Person {ID = 1, FirstName = "Magnus", LastName = "Hedlund"};
    Person terry = new Person {ID = 2, FirstName = "Terry", LastName = "Adams"};
    Person charlotte = new Person {ID = 3, FirstName = "Charlotte", LastName = "Weiss"};
    Person arlene = new Person {ID = 4, FirstName = "Arlene", LastName = "Huff"};

    Pet barley = new Pet {Name = "Barley", Owner = terry};
    Pet boots = new Pet {Name = "Boots", Owner = terry};
    Pet whiskers = new Pet {Name = "Whiskers", Owner = charlotte};
    Pet bluemoon = new Pet {Name = "Blue Moon", Owner = terry};
    Pet daisy = new Pet {Name = "Daisy", Owner = magnus};

    // Create two lists.
    List<Person> people = new List<Person> {magnus, terry, charlotte, arlene};
    List<Pet> pets = new List<Pet> {barley, boots, whiskers, bluemoon, daisy};

    var query = from person in people
        where person.ID == 4
        join pet in pets on person equals pet.Owner  into personpets
        from petOrNull in personpets.DefaultIfEmpty()
        select new { Person=person, Pet = petOrNull}; 



    foreach (var v in query )
    {
        Console.WriteLine("{0,-15}{1}", v.Person.FirstName + ":", (v.Pet == null ? "Does not Exist" : v.Pet.Name));
    }
}

// This code produces the following output:
//
// Magnus:        Daisy
// Terry:         Barley
// Terry:         Boots
// Terry:         Blue Moon
// Charlotte:     Whiskers
// Arlene:

ahora puede include elements from the leftincluso si ese elemento has no matches in the right, en nuestro caso, recuperamosArlene incluso él no tiene coincidencia en la derecha

aqui esta la referencia

Cómo: Realizar uniones externas izquierdas (Guía de programación de C #)

Basheer AL-MOMANI
fuente
la salida debería ser: Arlene: no existe
usuario1169587
10

Esta es la forma general (como ya se proporcionó en otras respuestas)

var c =
    from a in alpha
    join b in beta on b.field1 equals a.field1 into b_temp
    from b_value in b_temp.DefaultIfEmpty()
    select new { Alpha = a, Beta = b_value };

Sin embargo, aquí hay una explicación que espero aclare lo que esto realmente significa.

join b in beta on b.field1 equals a.field1 into b_temp

esencialmente crea un conjunto de resultados separado b_temp que efectivamente incluye 'filas' nulas para entradas en el lado derecho (entradas en 'b').

Luego la siguiente línea:

from b_value in b_temp.DefaultIfEmpty()

..itera sobre ese conjunto de resultados, configurando el valor nulo predeterminado para la 'fila' en el lado derecho, y configurando el resultado de la unión de la fila del lado derecho al valor de 'b_value' (es decir, el valor que está a la derecha lado de la mano, si hay un registro coincidente, o 'nulo' si no lo hay).

Ahora, si el lado derecho es el resultado de una consulta LINQ separada, consistirá en tipos anónimos, que solo pueden ser 'algo' o 'nulo'. Sin embargo, si es enumerable (por ejemplo, una Lista, donde MyObjectB es una clase con 2 campos), entonces es posible ser específico acerca de qué valores 'nulos' predeterminados se usan para sus propiedades:

var c =
    from a in alpha
    join b in beta on b.field1 equals a.field1 into b_temp
    from b_value in b_temp.DefaultIfEmpty( new MyObjectB { Field1 = String.Empty, Field2 = (DateTime?) null })
    select new { Alpha = a, Beta_field1 = b_value.Field1, Beta_field2 = b_value.Field2 };

Esto garantiza que 'b' en sí mismo no sea nulo (pero sus propiedades pueden ser nulas, utilizando los valores nulos predeterminados que ha especificado), y esto le permite verificar las propiedades de b_value sin obtener una excepción de referencia nula para b_value. Tenga en cuenta que para un DateTime anulable, un tipo de (DateTime?), Es decir, 'DateTime anulable' debe especificarse como el 'Tipo' del nulo en la especificación de 'DefaultIfEmpty' (esto también se aplicará a los tipos que no son 'nativos' 'nulables, por ejemplo, doble, flotante).

Puede realizar múltiples combinaciones externas izquierdas simplemente encadenando la sintaxis anterior.

Chris Halcrow
fuente
1
¿De dónde viene b_value?
Jack Fraser
9

Aquí hay un ejemplo si necesita unir más de 2 tablas:

from d in context.dc_tpatient_bookingd
join bookingm in context.dc_tpatient_bookingm 
     on d.bookingid equals bookingm.bookingid into bookingmGroup
from m in bookingmGroup.DefaultIfEmpty()
join patient in dc_tpatient
     on m.prid equals patient.prid into patientGroup
from p in patientGroup.DefaultIfEmpty()

Ref: https://stackoverflow.com/a/17142392/2343

Sameer Alibhai
fuente
4

Método de extensión que funciona como combinación izquierda con sintaxis Join

public static class LinQExtensions
{
    public static IEnumerable<TResult> LeftJoin<TOuter, TInner, TKey, TResult>(
        this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, 
        Func<TOuter, TKey> outerKeySelector, 
        Func<TInner, TKey> innerKeySelector, 
        Func<TOuter, TInner, TResult> resultSelector)
    {
        return outer.GroupJoin(
            inner, 
            outerKeySelector, 
            innerKeySelector,
            (outerElement, innerElements) => resultSelector(outerElement, innerElements.FirstOrDefault()));
    }
}

acaba de escribirlo en .NET core y parece estar funcionando como se esperaba.

Pequeña prueba:

        var Ids = new List<int> { 1, 2, 3, 4};
        var items = new List<Tuple<int, string>>
        {
            new Tuple<int, string>(1,"a"),
            new Tuple<int, string>(2,"b"),
            new Tuple<int, string>(4,"d"),
            new Tuple<int, string>(5,"e"),
        };

        var result = Ids.LeftJoin(
            items,
            id => id,
            item => item.Item1,
            (id, item) => item ?? new Tuple<int, string>(id, "not found"));

        result.ToList()
        Count = 4
        [0]: {(1, a)}
        [1]: {(2, b)}
        [2]: {(3, not found)}
        [3]: {(4, d)}
Bezio
fuente
4

Aquí hay una versión bastante fácil de entender usando la sintaxis del método:

IEnumerable<JoinPair> outerLeft =
    lefts.SelectMany(l => 
        rights.Where(r => l.Key == r.Key)
              .DefaultIfEmpty(new Item())
              .Select(r => new JoinPair { LeftId = l.Id, RightId = r.Id }));
Tim Pohlmann
fuente
3

Hay tres tablas: personas, escuelas y personas_escuelas, que conecta a las personas con las escuelas en las que estudian. Una referencia a la persona con id = 6 está ausente en la tabla personas_escuelas. Sin embargo, la persona con id = 6 se presenta en la cuadrícula de resultados unidos a la izquierda.

List<Person> persons = new List<Person>
{
    new Person { id = 1, name = "Alex", phone = "4235234" },
    new Person { id = 2, name = "Bob", phone = "0014352" },
    new Person { id = 3, name = "Sam", phone = "1345" },
    new Person { id = 4, name = "Den", phone = "3453452" },
    new Person { id = 5, name = "Alen", phone = "0353012" },
    new Person { id = 6, name = "Simon", phone = "0353012" }
};

List<School> schools = new List<School>
{
    new School { id = 1, name = "Saint. John's school"},
    new School { id = 2, name = "Public School 200"},
    new School { id = 3, name = "Public School 203"}
};

List<PersonSchool> persons_schools = new List<PersonSchool>
{
    new PersonSchool{id_person = 1, id_school = 1},
    new PersonSchool{id_person = 2, id_school = 2},
    new PersonSchool{id_person = 3, id_school = 3},
    new PersonSchool{id_person = 4, id_school = 1},
    new PersonSchool{id_person = 5, id_school = 2}
    //a relation to the person with id=6 is absent
};

var query = from person in persons
            join person_school in persons_schools on person.id equals person_school.id_person
            into persons_schools_joined
            from person_school_joined in persons_schools_joined.DefaultIfEmpty()
            from school in schools.Where(var_school => person_school_joined == null ? false : var_school.id == person_school_joined.id_school).DefaultIfEmpty()
            select new { Person = person.name, School = school == null ? String.Empty : school.name };

foreach (var elem in query)
{
    System.Console.WriteLine("{0},{1}", elem.Person, elem.School);
}
Alex
fuente
Si bien esta es quizás la respuesta a la pregunta, proporcione alguna explicación sobre su respuesta :)
Amir
2

Esta es una sintaxis SQL en comparación con la sintaxis LINQ para uniones internas y externas externas. Izquierda combinación externa:

http://www.ozkary.com/2011/07/linq-to-entity-inner-and-left-joins.html

"El siguiente ejemplo hace una unión grupal entre producto y categoría. Esta es esencialmente la unión izquierda. La expresión into devuelve datos incluso si la tabla de categorías está vacía. Para acceder a las propiedades de la tabla de categorías, ahora debemos seleccionar del resultado enumerable agregando el desde cl en la declaración catList.DefaultIfEmpty ().

ozkary
fuente
1

Realizar uniones externas izquierdas en linq C # // Realizar uniones externas izquierdas

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

class Child
{
    public string Name { get; set; }
    public Person Owner { get; set; }
}
public class JoinTest
{
    public static void LeftOuterJoinExample()
    {
        Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
        Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
        Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
        Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };

        Child barley = new Child { Name = "Barley", Owner = terry };
        Child boots = new Child { Name = "Boots", Owner = terry };
        Child whiskers = new Child { Name = "Whiskers", Owner = charlotte };
        Child bluemoon = new Child { Name = "Blue Moon", Owner = terry };
        Child daisy = new Child { Name = "Daisy", Owner = magnus };

        // Create two lists.
        List<Person> people = new List<Person> { magnus, terry, charlotte, arlene };
        List<Child> childs = new List<Child> { barley, boots, whiskers, bluemoon, daisy };

        var query = from person in people
                    join child in childs
                    on person equals child.Owner into gj
                    from subpet in gj.DefaultIfEmpty()
                    select new
                    {
                        person.FirstName,
                        ChildName = subpet!=null? subpet.Name:"No Child"
                    };
                       // PetName = subpet?.Name ?? String.Empty };

        foreach (var v in query)
        {
            Console.WriteLine($"{v.FirstName + ":",-25}{v.ChildName}");
        }
    }

    // This code produces the following output:
    //
    // Magnus:        Daisy
    // Terry:         Barley
    // Terry:         Boots
    // Terry:         Blue Moon
    // Charlotte:     Whiskers
    // Arlene:        No Child

https://dotnetwithhamid.blogspot.in/

Hamid
fuente
1

Me gustaría agregar que si obtiene la extensión MoreLinq ahora hay soporte para uniones izquierdas homogéneas y heterogéneas ahora

http://morelinq.github.io/2.8/ref/api/html/Overload_MoreLinq_MoreEnumerable_LeftJoin.htm

ejemplo:

//Pretend a ClientCompany object and an Employee object both have a ClientCompanyID key on them

return DataContext.ClientCompany
    .LeftJoin(DataContext.Employees,                         //Table being joined
        company => company.ClientCompanyID,                  //First key
        employee => employee.ClientCompanyID,                //Second Key
        company => new {company, employee = (Employee)null}, //Result selector when there isn't a match
        (company, employee) => new { company, employee });   //Result selector when there is a match

EDITAR:

En retrospectiva, esto puede funcionar, pero convierte el IQueryable en un IEnumerable ya que morelinq no convierte la consulta a SQL.

En su lugar, puede usar un GroupJoin como se describe aquí: https://stackoverflow.com/a/24273804/4251433

Esto asegurará que permanezca como IQueryable en caso de que necesite realizar más operaciones lógicas en él más adelante.

Reese De Wind
fuente
1

La manera fácil es usar la palabra clave Let. Esto funciona para mi.

from AItem in Db.A
Let BItem = Db.B.Where(x => x.id == AItem.id ).FirstOrDefault() 
Where SomeCondition
Select new YourViewModel
{
    X1 = AItem.a,
    X2 = AItem.b,
    X3 = BItem.c
}

Esta es una simulación de Left Join. Si cada elemento de la tabla B no coincide con el elemento A, BItem devuelve nulo

Mahdi Moghimi
fuente
0

Si necesita unirse y filtrar algo, puede hacerlo fuera de la unión. El filtro se puede hacer después de crear la colección.

En este caso, si hago esto en la condición de unión, reduzco las filas que se devuelven.

Se usa condición ternaria (= n == null ? "__" : n.MonDayNote,)

  • Si el objeto es null(entonces no coincide), entonces devuelve lo que está después de ?. __, en este caso.

  • Si no, regrese lo que es después de la :, n.MonDayNote.

Gracias a los otros colaboradores que es donde comencé con mi propio problema.


        var schedLocations = (from f in db.RAMS_REVENUE_LOCATIONS
              join n in db.RAMS_LOCATION_PLANNED_MANNING on f.revenueCenterID equals

                  n.revenueCenterID into lm

              from n in lm.DefaultIfEmpty()

              join r in db.RAMS_LOCATION_SCHED_NOTE on f.revenueCenterID equals r.revenueCenterID
              into locnotes

              from r in locnotes.DefaultIfEmpty()
              where f.LocID == nLocID && f.In_Use == true && f.revenueCenterID > 1000

              orderby f.Areano ascending, f.Locname ascending
              select new
              {
                  Facname = f.Locname,
                  f.Areano,
                  f.revenueCenterID,
                  f.Locabbrev,

                  //  MonNote = n == null ? "__" : n.MonDayNote,
                  MonNote = n == null ? "__" : n.MonDayNote,
                  TueNote = n == null ? "__" : n.TueDayNote,
                  WedNote = n == null ? "__" : n.WedDayNote,
                  ThuNote = n == null ? "__" : n.ThuDayNote,

                  FriNote = n == null ? "__" : n.FriDayNote,
                  SatNote = n == null ? "__" : n.SatDayNote,
                  SunNote = n == null ? "__" : n.SunDayNote,
                  MonEmpNbr = n == null ? 0 : n.MonEmpNbr,
                  TueEmpNbr = n == null ? 0 : n.TueEmpNbr,
                  WedEmpNbr = n == null ? 0 : n.WedEmpNbr,
                  ThuEmpNbr = n == null ? 0 : n.ThuEmpNbr,
                  FriEmpNbr = n == null ? 0 : n.FriEmpNbr,
                  SatEmpNbr = n == null ? 0 : n.SatEmpNbr,
                  SunEmpNbr = n == null ? 0 : n.SunEmpNbr,
                  SchedMondayDate = n == null ? dMon : n.MondaySchedDate,
                  LocNotes = r == null ? "Notes: N/A" : r.LocationNote

              }).ToList();
                Func<int, string> LambdaManning = (x) => { return x == 0 ? "" : "Manning:" + x.ToString(); };
        DataTable dt_ScheduleMaster = PsuedoSchedule.Tables["ScheduleMasterWithNotes"];
        var schedLocations2 = schedLocations.Where(x => x.SchedMondayDate == dMon);
BionicCyborg
fuente
0
class Program
{
    List<Employee> listOfEmp = new List<Employee>();
    List<Department> listOfDepart = new List<Department>();

    public Program()
    {
        listOfDepart = new List<Department>(){
            new Department { Id = 1, DeptName = "DEV" },
            new Department { Id = 2, DeptName = "QA" },
            new Department { Id = 3, DeptName = "BUILD" },
            new Department { Id = 4, DeptName = "SIT" }
        };


        listOfEmp = new List<Employee>(){
            new Employee { Empid = 1, Name = "Manikandan",DepartmentId=1 },
            new Employee { Empid = 2, Name = "Manoj" ,DepartmentId=1},
            new Employee { Empid = 3, Name = "Yokesh" ,DepartmentId=0},
            new Employee { Empid = 3, Name = "Purusotham",DepartmentId=0}
        };

    }
    static void Main(string[] args)
    {
        Program ob = new Program();
        ob.LeftJoin();
        Console.ReadLine();
    }

    private void LeftJoin()
    {
        listOfEmp.GroupJoin(listOfDepart.DefaultIfEmpty(), x => x.DepartmentId, y => y.Id, (x, y) => new { EmpId = x.Empid, EmpName = x.Name, Dpt = y.FirstOrDefault() != null ? y.FirstOrDefault().DeptName : null }).ToList().ForEach
            (z =>
            {
                Console.WriteLine("Empid:{0} EmpName:{1} Dept:{2}", z.EmpId, z.EmpName, z.Dpt);
            });
    }
}

class Employee
{
    public int Empid { get; set; }
    public string Name { get; set; }
    public int DepartmentId { get; set; }
}

class Department
{
    public int Id { get; set; }
    public string DeptName { get; set; }
}

SALIDA

Manikandan Deivasigamani
fuente
0

Según mi respuesta a una pregunta similar, aquí:

Unión externa izquierda de Linq a SQL utilizando la sintaxis de Lambda y la unión en 2 columnas (clave de combinación compuesta)

Obtén el código aquí , o clona mi repositorio de github , ¡y juega!

Consulta:

        var petOwners =
            from person in People
            join pet in Pets
            on new
            {
                person.Id,
                person.Age,
            }
            equals new
            {
                pet.Id,
                Age = pet.Age * 2, // owner is twice age of pet
            }
            into pets
            from pet in pets.DefaultIfEmpty()
            select new PetOwner
            {
                Person = person,
                Pet = pet,
            };

Lambda

        var petOwners = People.GroupJoin(
            Pets,
            person => new { person.Id, person.Age },
            pet => new { pet.Id, Age = pet.Age * 2 },
            (person, pet) => new
            {
                Person = person,
                Pets = pet,
            }).SelectMany(
            pet => pet.Pets.DefaultIfEmpty(),
            (people, pet) => new
            {
                people.Person,
                Pet = pet,
            });
Adam Cox
fuente
0

Descripción general: en este fragmento de código, demuestro cómo agrupar por ID donde Table1 y Table2 tienen una relación de uno a muchos. Agrupo en Id, Field1 y Field2. La subconsulta es útil, si se requiere una tercera búsqueda de tabla y se hubiera requerido una relación de unión izquierda. Muestro una agrupación de unión izquierda y una subconsulta linq. Los resultados son equivalentes.

class MyView
{
public integer Id {get,set};
    public String Field1  {get;set;}
public String Field2 {get;set;}
    public String SubQueryName {get;set;}                           
}

IList<MyView> list = await (from ci in _dbContext.Table1
                                               join cii in _dbContext.Table2
                                                   on ci.Id equals cii.Id

                                               where ci.Field1 == criterion
                                               group new
                                               {
                                                   ci.Id
                                               } by new { ci.Id, cii.Field1, ci.Field2}

                                           into pg
                                               select new MyView
                                               {
                                                   Id = pg.Key.Id,
                                                   Field1 = pg.Key.Field1,
                                                   Field2 = pg.Key.Field2,
                                                   SubQueryName=
                                                   (from chv in _dbContext.Table3 where chv.Id==pg.Key.Id select chv.Field1).FirstOrDefault()
                                               }).ToListAsync<MyView>();


 Compared to using a Left Join and Group new

IList<MyView> list = await (from ci in _dbContext.Table1
                                               join cii in _dbContext.Table2
                                                   on ci.Id equals cii.Id

                       join chv in _dbContext.Table3
                                                  on cii.Id equals chv.Id into lf_chv
                                                from chv in lf_chv.DefaultIfEmpty()

                                               where ci.Field1 == criterion
                                               group new
                                               {
                                                   ci.Id
                                               } by new { ci.Id, cii.Field1, ci.Field2, chv.FieldValue}

                                           into pg
                                               select new MyView
                                               {
                                                   Id = pg.Key.Id,
                                                   Field1 = pg.Key.Field1,
                                                   Field2 = pg.Key.Field2,
                                                   SubQueryName=pg.Key.FieldValue
                                               }).ToListAsync<MyView>();
Leon de Oro
fuente
-1
(from a in db.Assignments
     join b in db.Deliveryboys on a.AssignTo equals b.EmployeeId  

     //from d in eGroup.DefaultIfEmpty()
     join  c in  db.Deliveryboys on a.DeliverTo equals c.EmployeeId into eGroup2
     from e in eGroup2.DefaultIfEmpty()
     where (a.Collected == false)
     select new
     {
         OrderId = a.OrderId,
         DeliveryBoyID = a.AssignTo,
         AssignedBoyName = b.Name,
         Assigndate = a.Assigndate,
         Collected = a.Collected,
         CollectedDate = a.CollectedDate,
         CollectionBagNo = a.CollectionBagNo,
         DeliverTo = e == null ? "Null" : e.Name,
         DeliverDate = a.DeliverDate,
         DeliverBagNo = a.DeliverBagNo,
         Delivered = a.Delivered

     });
saktiprasad swain
fuente
-1

Solución simple para la IZQUIERDA EXTERIOR IZQUIERDA :

var setA = context.SetA;
var setB = context.SetB.Select(st=>st.Id).Distinct().ToList();
var leftOuter  = setA.Where(stA=> !setB.Contains(stA.Id)); 

notas :

  • Para mejorar el rendimiento, SetB podría convertirse en un diccionario (si eso se hace, entonces debe cambiar esto :! SetB.Contains (stA.Id) ) o un HashSet
  • Cuando hay más de un campo involucrado, esto se puede lograr usando operaciones Set y una clase que implemente: IEqualityComparer
Enrique Ramos
fuente
Una combinación externa izquierda devolvería la coincidencia setAy setBla respuesta.
NetMage