2010-08-04 9 views
341

¿Cómo realizar la unión externa izquierda en C# LINQ a los objetos sin utilizar las cláusulas join-on-equals-into? ¿Hay alguna manera de hacerlo con la cláusula where? problema correcta: Para combinación interna es fácil y tengo una solución como estaUNIÓN EXTERIOR EXTERIOR en LINQ

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 externa izquierda Necesito una solución. El mío es algo parecido a esto, pero no está funcionando

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; } 
+2

se puede dar un ejemplo de lo que estamos tratando de lograr? – jeroenh

+0

exterior izquierdo normal unirse es algo como esto: var a = de b en bb unen c en cc en b.bbbbb iguales c.ccccc en dd de d en dd.DefaultIfEmpty() seleccione b.sss; pregunta mía es que hay alguna manera de hacer que el uso de unirse a witouth-a-igual-en cláusulas algo como esto var a = b a partir de bb de C en cc donde b.bbb == c.cccc ... y así sucesivamente ... – Toy

+1

seguro que sí, pero debe publicar un ejemplo del código que ya tiene para que la gente pueda darle una mejor respuesta – sloth

Respuesta

32

Tome un vistazo a este example. Esta consulta debería funcionar:

var leftFinal = 
     from l in lefts 
     join r in rights on l equals r.Left into lrs 
     from lr in lrs.DefaultIfEmpty() 
     select new { LeftId = l.Id, RightId = ((l.Key==r.Key) ? r.Id : 0 }; 
+3

¿Se puede acceder a 'r' en la cláusula de selección después de usar una combinación? –

+0

@FarhadAlizadehNoori Sí, puede. –

409

Como se indica en:

101 LINQ Samples - Left outer join

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 }; 
+5

Estoy intentando lo mismo pero obtengo un error en el operador de unión, que dice "El tipo de una de las expresiones en la cláusula join es incorrecto". –

+2

@jain si sus tipos son diferentes, la unión no funcionará.Es probable que sus claves sean de tipos de datos diferentes. ¿Son ambas teclas int por ejemplo? – Yooakim

+0

Sí, fue, por cierto, que obtuve la solución. Gracias por tu tiempo. –

75

El uso de 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 
    }) 
+0

¿Tiene que usar '.GroupJoin' o también puede solo usa '.Join'? – Jess

+3

Ambos, Join y GroupJoin realmente no admiten la combinación izquierda. El truco para usar GroupJoin es que puedes tener grupos vacíos y luego traducir esos grupos vacíos a valores vacíos. DefaultIfEmpty simplemente hace eso, lo que significa 'Enumerable.Empty .DefaultIfEmpty()' devolverá un IEnumerable con un valor único de 'default (Product)'. –

+25

Todo esto para realizar una unión a la izquierda ?? –

15

Una implementación de combinación externa izquierda por métodos de extensión podría buscar 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 encargarse 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)); 
    } 
+4

Esta es * solo * una opción para LINQ para objetos sin embargo, 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. – Servy

+13

Pero la pregunta era "Cómo realizar la combinación externa izquierda en C# LINQ a _objetos_ ..." – Bertrand

366

Necromancing.
Si se utiliza un proveedor de LINQ con base de datos, una combinación externa significativamente más legible combinación izquierda puede escribirse como tal:

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

si se omite el DefaultIfEmpty() que tendrá una combinació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 confuso, y no está claro cómo funciona cuando se quiere unir tablas izquierda MÚLTIPLES.

Nota
Cabe señalar que from alias in Repo.whatever.Where(condition).DefaultIfEmpty() es el mismo que un exterior a aplicar/izquierda-unirse-lateral, que cualquier (no retardada) de base de datos-optimizador es perfectamente capaz de traducir en un izquierda unirse , siempre y cuando no introduzca valores por fila (también conocido como una aplicación externa real). No haga esto en Linq-2-Objects (porque no hay DB-optimizer cuando usa Linq-to-Objects).

detallada Ejemplo

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 utiliza 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:

Ver también " Convert SQL Server query to Linq query " para un ejemplo más complejo.

Además, si lo hace en Linq-2-Objects (en lugar de Linq-2-SQL), debe hacerlo a la antigua (porque LINQ to SQL traduce esto correctamente para unir operaciones, pero sobre los objetos de este método obliga a un análisis completo, y no toma ventaja de búsquedas de índice, whyever ...):

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 
    } 

); 
+13

Esta respuesta es realmente útil. Gracias por ofrecer una sintaxis comprensible. –

+2

¡Tantos años haciendo uniones izquierdas para el otro lado! ¡Gracias! – Todd

+3

WTB una consulta LINQ compatible con NHibernate ... :) – mxmissile

2

esta es una sintaxis SQL compara con la sintaxis de LINQ para combinaciones externas interior y la izquierda. combinación externa izquierda:.

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

"El siguiente ejemplo hace un grupo de unión entre producto y categoría Esta es esencialmente la izquierda unirse a los en la expresión devuelve datos incluso si la tabla de categorías está vacía Para acceder al.. propiedades de la tabla de categorías, ahora hay que elegir entre el resultado enumerables añadiendo el de CL en catList.DefaultIfEmpty declaración()

6

He aquí un ejemplo, si usted necesita para unirse a 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

4

Hay tres tablas:. Personas, escuelas y persons_schools, que conecta personas a las escuelas que estudian en una referencia a la persona con id = 6 está ausente en las persons_schools mesa. Sin embargo, la persona con id = 6 se presenta en la cuadrícula de resultado lef-joined.

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); 
} 
+0

Si bien esta es quizás la respuesta a la pregunta, brinde alguna explicación sobre su respuesta :) – Amir

16

Ahora, como un 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)); 
    } 
} 

uso como lo haría normalmente el uso de Ingreso:

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

Hope esto le ahorra tiempo.

5

toma vistazo a 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)); 
    } 
} 

aquí es la referencia

How to: Perform Left Outer Joins (C# Programming Guide)

1

Si necesita unirse y filtro en algo, que se puede hacer 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.

condición ternario se utiliza (= n == null ? "__" : n.MonDayNote,)

  • Si el objeto es null (por lo que no puede competir), a continuación, devolver lo que es después de la ?. __, en este caso.

  • De lo contrario, devuelva lo que está después del :, 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); 
0
(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 

    }); 
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; } 
} 

OUTPUT

+0

en lugar de un captura de pantalla por favor. copia y pega la salida directamente a tu respuesta – jps

4

Esta es la forma general (como ya se prevé 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í es una explicación que espero aclarar ¡qué significa esto realmente!

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

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

Entonces la línea siguiente:

from b_value in b_temp.DefaultIfEmpty() 

..iterates más de ese conjunto de resultados, fijando el valor nulo por defecto de la 'fila' en el lado derecho, y estableciendo el resultado de la fila derecha unirse al valor de 'b_value' (es decir, el valor que está en el lado derecho, 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'. Si es sin embargo un enumerable (por ejemplo, una lista - en donde MyObjectB es una clase con 2 campos), entonces es posible ser específico acerca de lo valores por defecto 'nulo' se usan por 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 asegura que ' b 'en sí mismo no es nulo (pero sus propiedades pueden ser nulas, usando los valores nulos predeterminados que haya 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 con nulos, un tipo de (DateTime?) Ie 'NULL DateTime' se debe especificar como el 'Type' del null en la especificación para 'DefaultIfEmpty' (esto también se aplicará a los tipos que no son 'nativos' 'nulable, por ejemplo, doble, flotación).

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

0

solución simple para el LEFT OUTER JOIN:

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 SETB rendimiento podría ser convertido en un diccionario(si se hace eso, entonces tiene que cambiar esto: ! setB.Contains (stA.Id)) o HashSet
  • Cuando hay más de un campo involucrado esto se podría lograr con Conjunto operaciones y una clase que implementan: IEqualityComparer método de extensión
0

que funciona como izquierda se unen con la sintaxis de combinación

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)} 
0

me gustaría añadir que si se obtiene la extensión MoreLinq ahora hay soporte para ambos izquierda homogénea y heterogénea se une 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 
0

Realice la salida hacia la izquierda ter une en LINQ C# // Realizar externa izquierda se une

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/

Cuestiones relacionadas