So führen Sie Joins in LINQ für mehrere Felder in einem einzelnen Join durch


244

Ich muss eine LINQ2DataSet-Abfrage durchführen, die einen Join für mehr als ein Feld ausführt (as

var result = from x in entity
join y in entity2 
       on x.field1 = y.field1 
and 
          x.field2 = y.field2

Ich habe noch eine geeignete Lösung gefunden (ich kann die zusätzlichen Einschränkungen zu einer where-Klausel hinzufügen, aber dies ist weit von einer geeigneten Lösung entfernt, oder ich verwende diese Lösung, aber dies setzt ein Äquijoin voraus).

Ist es in LINQ möglich, in einem einzigen Join mehreren Feldern beizutreten?

BEARBEITEN

var result = from x in entity
             join y in entity2
             on new { x.field1, x.field2 } equals new { y.field1, y.field2 }

ist die Lösung, auf die ich oben Bezug genommen habe.

Weitere EDIT

Um die Kritik zu beantworten, dass mein ursprüngliches Beispiel ein Equijoin war, erkenne ich an, dass meine derzeitige Anforderung ein Equijoin ist und ich bereits die Lösung verwendet habe, auf die ich oben verwiesen habe.

Ich versuche jedoch zu verstehen, welche Möglichkeiten und Best Practices ich bei LINQ habe / anwenden sollte. Ich muss bald einen Datumsbereichsabfrage-Join mit einer Tabellen-ID durchführen und habe dieses Problem nur vorweggenommen. Es sieht so aus, als müsste ich den Datumsbereich in der where-Klausel hinzufügen.

Vielen Dank wie immer für alle Vorschläge und Kommentare


48
Nur eine Information für jeden, der dies liest. Wenn Sie einen Mehrfeld-Join in Annon-Klassen durchführen, MÜSSEN Sie die Felder in beiden Annon-Klassen gleich benennen, da sonst Kompilierungsfehler auftreten.
Mark

6
Oder besser gesagt, Sie müssen sicherstellen, dass sie übereinstimmende Namen haben. Das heißt, Sie können einfach die Felder eines der Anontypen benennen, damit sie mit dem anderen übereinstimmen.
Tom Ferguson


Ich habe eher Tupel für beide Seiten der Gleichen als für Objekte verwendet, und es schien auch zu funktionieren.
GHZ

Antworten:


89

Die Lösung mit dem anonymen Typ sollte gut funktionieren. LINQ kann nur Equijoins darstellen (jedenfalls mit Join-Klauseln), und tatsächlich haben Sie gesagt, dass Sie dies auf der Grundlage Ihrer ursprünglichen Abfrage sowieso ausdrücken möchten.

Wenn Ihnen die Version mit dem anonymen Typ aus einem bestimmten Grund nicht gefällt, sollten Sie diesen Grund erläutern.

Wenn Sie etwas anderes als das tun möchten, wonach Sie ursprünglich gefragt haben, geben Sie bitte ein Beispiel dafür, was Sie wirklich tun möchten.

BEARBEITEN: Antworten auf die Bearbeitung in der Frage: Ja, um einen "Datumsbereich" -Verbindungsvorgang durchzuführen, müssen Sie stattdessen eine where-Klausel verwenden. Sie sind wirklich semantisch äquivalent, es ist also nur eine Frage der verfügbaren Optimierungen. Equijoins bieten eine einfache Optimierung (in LINQ to Objects, einschließlich LINQ to DataSets), indem eine Suche basierend auf der inneren Sequenz erstellt wird. Stellen Sie sich diese als Hashtabelle vom Schlüssel zu einer Sequenz von Einträgen vor, die diesem Schlüssel entsprechen.

Das mit Datumsbereichen zu tun ist etwas schwieriger. Abhängig davon, was genau Sie unter einem "Datumsbereich-Join" verstehen, können Sie möglicherweise etwas Ähnliches tun - wenn Sie vorhaben, "Datumsbereiche" (z. B. einen pro Jahr) so zu erstellen, dass zwei Einträge in der Das gleiche Jahr (aber nicht am selben Datum) sollte übereinstimmen. Dann können Sie dies einfach tun, indem Sie dieses Band als Schlüssel verwenden. Wenn es komplizierter ist, z. B. eine Seite des Joins einen Bereich bereitstellt und die andere Seite des Joins ein einzelnes Datum bereitstellt, das übereinstimmt, wenn es in diesen Bereich fällt, wird dies besser mit einer whereKlausel behandelt (nach einer Sekunde)fromKlausel) IMO. Sie könnten besonders funky zaubern, indem Sie der einen oder anderen Seite befehlen, Übereinstimmungen effizienter zu finden, aber das wäre eine Menge Arbeit - ich würde so etwas erst tun, nachdem ich überprüft habe, ob die Leistung ein Problem darstellt.


Danke, ja, die Leistung ist meine Hauptsorge bei der Verwendung der where-Klausel. Ich vermute, dass eine where-Klausel nach dem Join einen Filter für ein größeres Dataset ausführen würde, das durch die Einführung des zweiten Join-Parameters hätte reduziert werden können. Ich mag die Idee, zu bestellen, um zu testen, ob ich Effizienzgewinne erzielen kann
Johnc

Wie viele Datensätze werden Sie haben? Vergessen Sie nicht, dass die Bestellung der Ergebnisse zunächst eine gewisse Zeit in Anspruch nimmt ...
Jon Skeet

"Sie sind wirklich semantisch äquivalent" - brauchen wir dort das Wort "wirklich"? Vielleicht meinten Sie: "Sie sind wirklich semantisch äquivalent" :)
Tag, wenn

136
var result = from x in entity
   join y in entity2 on new { x.field1, x.field2 } equals new { y.field1, y.field2 }

Dies ist, wonach ich gesucht habe, da die 101 Linq-Proben dies nicht hatten oder zumindest das, was ich gesehen habe.
Chris Marisic

1
@ PeterX in der Tat kann es, siehe meine Antwort hier: stackoverflow.com/a/22176658/595157
niieani

13
Der obige Code hat nicht funktioniert. Nach dem Hinzufügen on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 } funktionierte es
Ravi Ram

@ Ravi Ram .. Danke .. Ihr Kommentar hat geholfen
NMathur

80
var result = from x in entity1
             join y in entity2
             on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }

Sie müssen dies tun, wenn sich die Spaltennamen in zwei Entitäten unterscheiden.


6
Vielen Dank für die Erwähnung der verschiedenen Spaltennamen. Dies reparierte meinen schlechten Ausdruck.
Gaʀʀʏ

1
Das hat auch bei mir funktioniert. Wenn die Spaltennamen nicht übereinstimmen, wird folgende Fehlermeldung angezeigt: "Der Typ eines der Ausdrücke in der Join-Klausel ist falsch. Die Typinferenz ist beim Aufruf von 'GroupJoin' fehlgeschlagen."
Humbads

Vielen Dank für das Aliasing der Schlüsselvariablen.
Thomas.Benz

Ich habe den Fehler erhalten, den @humbads erwähnt hat, als ich nicht alle Eigenschaften von int als 'new {}' bezeichnet habe. Also nur zu Ihrer Information, wenn Sie einen nennen, müssen Sie auch den Rest benennen.
Ethan Melamed

Vielen Dank
Charly H

51

Um dies mit einer äquivalenten Methodenkettensyntax zu vervollständigen:

entity.Join(entity2, x => new {x.Field1, x.Field2},
                     y => new {y.Field1, y.Field2}, (x, y) => x);

Während das letzte Argument das (x, y) => xist, was Sie auswählen (im obigen Fall wählen wir x).


31

Ich denke, eine lesbarere und flexiblere Option ist die Verwendung der Where-Funktion:

var result = from x in entity1
             from y in entity2
                 .Where(y => y.field1 == x.field1 && y.field2 == x.field2)

Dies ermöglicht auch den einfachen Wechsel vom inneren zum linken Join durch Anhängen von .DefaultIfEmpty ().


Als langjähriger Lambda-Benutzer (im Gegensatz zu dem Zeitpunkt, als ich die Frage gestellt habe) müsste ich zustimmen
Johnc

Wäre das langsamer?
AlfredBr

1
Ich denke, es sollte die gleiche Leistung wie die neue { ... } equals new { ... }Syntax haben. LinqPad ist ein großartiges Tool, um zu sehen, wie sich Ausdrücke verhalten (SQL-Skript, wenn LINQ2SQL verwendet wird, Ausdrucksbäume usw.)
Alexei

Soweit ich bemerkt habe, produziert es CROSS JOIN anstelle von INNER JOIN
Mariusz

@Mariusz Ja, es ist sinnvoll, CROSS JOIN + WHERE anstelle von INNER JOIN zu generieren. Für einfache Abfragen erwarte ich jedoch, dass der Analysator eine sehr ähnliche generiert.
Alexei

10
var result = from x in entity
             join y in entity2
             on new { X1= x.field1, X2= x.field2 } equals new { X1=y.field1, X2= y.field2 }
             select new 
             {
               /// Columns
              };

8

Sie könnten so etwas wie (unten) tun

var query = from p in context.T1

        join q in context.T2

        on

        new { p.Col1, p.Col2 }

        equals

         new { q.Col1, q.Col2 }

        select new {p...., q......};

Wie ich in Frage erwähnt habe, erfordert dies ein Equijoin
Johnc

7

Mit dem Join-Operator können Sie nur Equijoins ausführen. Andere Arten von Verknüpfungen können mit anderen Operatoren erstellt werden. Ich bin mir nicht sicher, ob der genaue Join, den Sie versuchen, mit diesen Methoden oder durch Ändern der where-Klausel einfacher wäre. Die Dokumentation zur Join-Klausel finden Sie hier . MSDN enthält einen Artikel zu Verknüpfungsvorgängen mit mehreren Links zu Beispielen für andere Verknüpfungen.


3

Wenn sich die Feldnamen in Entitäten unterscheiden

var result = from x in entity
   join y in entity2 on 
          new {
                field1=   x.field1,
               field2 =  x.field2 
             } 
          equals
         new { 
                field1= y.field1,
                field2=  y.myfield
              }
select new {x,y});

Danke dir. Namensübereinstimmung war das Stück, das mir fehlte.
Brett

2

Als vollständige Methodenkette würde das so aussehen:

lista.SelectMany(a => listb.Where(xi => b.Id == a.Id && b.Total != a.Total),
                (a, b) => new ResultItem
                {
                    Id = a.Id,
                    ATotal = a.Total,
                    BTotal = b.Total
                }).ToList();

-2
from d in db.CourseDispatches
                             join du in db.DispatchUsers on d.id equals du.dispatch_id
                             join u in db.Users on du.user_id equals u.id
                             join fr in db.Forumreports on (d.course_id + '_' + du.user_id)  equals  (fr.course_id + '_'+ fr.uid)

das funktioniert bei mir


Dies ist für mehrere Joins, er möchte einen Join mit mehreren Feldern in einem einzigen Join machen
theLaw

-3

Deklarieren Sie eine Klasse (Typ), die die Elemente enthält, denen Sie beitreten möchten. Im folgenden Beispiel deklarieren Sie JoinElement

 public class **JoinElement**
{
    public int? Id { get; set; }
    public string Name { get; set; }

}

results = from course in courseQueryable.AsQueryable()
                  join agency in agencyQueryable.AsQueryable()
                   on new **JoinElement**() { Id = course.CourseAgencyId, Name = course.CourseDeveloper } 
                   equals new **JoinElement**() { Id = agency.CourseAgencyId, Name = "D" } into temp1

1
Dies wurde bereits vor 9 Jahren beantwortet ... Welchen Wert bringt diese Antwort?
Maciej Jureczko
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.