664 Stimmen

LEFT OUTER JOIN in LINQ

Wie führt man eine linke äußere Verknüpfung in C# LINQ to objects ohne Verwendung von join-on-equals-into Klauseln? Gibt es eine Möglichkeit, dies zu tun mit where Klausel? Richtiges Problem: Für inner join ist einfach und ich habe eine Lösung wie diese

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

aber für die linke äußere Verbindung brauche ich eine Lösung. Meine ist etwas wie dieses, aber es funktioniert nicht

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 ist eine Klasse:

public class JoinPair { long leftId; long rightId; }

3 Stimmen

Können Sie ein Beispiel dafür geben, was Sie zu erreichen versuchen?

0 Stimmen

Normale linke äußere Verknüpfung ist so etwas wie dieses: var a = from b in bb join c in cc on b.bbbbb equals c.ccccc into dd from d in dd.DefaultIfEmpty() select b.sss; Meine Frage ist es eine Möglichkeit zu tun, dass ohne Verwendung von join-on-equals-into-Klauseln etwas wie dieses var a = from b in bb from c in cc where b.bbb == c.cccc ... und so weiter...

2 Stimmen

Sicher gibt es, aber Sie sollten ein Beispiel für Ihren Code posten, den Sie bereits haben, damit die Leute Ihnen eine bessere Antwort geben können

20voto

Bertrand Punkte 495

Eine Implementierung der linken äußeren Verknüpfung durch Erweiterungsmethoden könnte wie folgt aussehen

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

Der Ergebnisselektor muss sich dann um die Nullelemente kümmern. 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));
   }

5 Stimmen

Dies ist nur eine Option für LINQ to objects und kann die Abfrage nicht in beliebige Abfrageanbieter übersetzen, was der häufigste Anwendungsfall für diesen Vorgang ist.

16 Stimmen

Aber die Frage war: "Wie führt man Left Outer Join in C# LINQ to Objekte ..."

1 Stimmen

Der letzte Parameter "comparer" in der LeftJoin-Methode sollte ein optionaler Parameter sein, der gleich Null ist, denke ich.

13voto

Basheer AL-MOMANI Punkte 13273

Sehen Sie sich dieses Beispiel an

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:

jetzt können Sie include elements from the left auch wenn dieses Element has no matches in the right In unserem Fall haben wir folgende Ergebnisse erzielt Arlene selbst er hat kein passendes im Recht

hier ist die Referenz

So geht's: Left Outer Joins durchführen (C# Programmierhandbuch)

0 Stimmen

Sollte die Ausgabe sein: Arlene: Existiert nicht

12voto

Chris Halcrow Punkte 25120

Dies ist die allgemeine Form (wie bereits in anderen Antworten angegeben)

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

Hier ist jedoch eine Erklärung, die hoffentlich klarstellt, was das eigentlich bedeutet!

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

erzeugt im Wesentlichen eine separate Ergebnismenge b_temp, die effektiv Null-"Zeilen" für Einträge auf der rechten Seite (Einträge in "b") enthält.

Dann die nächste Zeile:

from b_value in b_temp.DefaultIfEmpty()

durchläuft diese Ergebnismenge, setzt den Standardwert Null für die "Zeile" auf der rechten Seite und setzt das Ergebnis der rechten Zeilenverknüpfung auf den Wert von "b_value" (d. h. den Wert auf der rechten Seite, wenn es einen übereinstimmenden Datensatz gibt, oder "Null", wenn es keinen gibt).

Wenn nun die rechte Seite das Ergebnis einer separaten LINQ-Abfrage ist, besteht sie aus anonymen Typen, die nur entweder "etwas" oder "null" sein können. Wenn es sich jedoch um eine Aufzählung handelt (z. B. eine Liste - wobei MyObjectB eine Klasse mit 2 Feldern ist), dann ist es möglich, genau festzulegen, welche Standard-Nullwerte für die Eigenschaften verwendet werden:

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

Dies stellt sicher, dass 'b' selbst nicht null ist (aber seine Eigenschaften können null sein, unter Verwendung der Standard-Nullwerte, die Sie angegeben haben), und dies ermöglicht Ihnen, Eigenschaften von b_value zu überprüfen, ohne eine Null-Referenz-Ausnahme für b_value zu erhalten. Beachten Sie, dass für eine nullable DateTime ein Typ von (DateTime?) d.h. 'nullable DateTime' als 'Type' der Null in der Spezifikation für 'DefaultIfEmpty' angegeben werden muss (dies gilt auch für Typen, die nicht 'nativ' nullable sind, z.B. double, float).

Sie können mehrere Left Outer Joins durchführen, indem Sie die obige Syntax einfach aneinanderreihen.

3 Stimmen

Woher kommt der b_value?

11voto

Sameer Alibhai Punkte 2952

Hier ist ein Beispiel, wenn Sie mehr als 2 Tabellen verbinden müssen:

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

8voto

Tim Pohlmann Punkte 3714

Hier ist eine relativ leicht verständliche Version, die die Methodensyntax verwendet:

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

3 Stimmen

Amüsant, wie es einfacher ist, LINQ-Funktionen zu vermeiden, die das Wort "join" in ihrem Namen enthalten

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X