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

Johnc
quelle
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
1
Achten Sie
TS
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.

Jon Skeet
quelle
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 }
KristoferA
quelle
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.

RealNapster
quelle
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).

niieani
quelle
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 ().

Alexei
quelle
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
              };
user3966657
quelle
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......};
Perpetualcoder
quelle
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.

Tvanfosson
quelle
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});
Mahesh
quelle
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();
Adam Garner
quelle
-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

user2745564
quelle
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
Ven
quelle
1
Dies wurde bereits vor 9 Jahren beantwortet ... Welchen Wert bringt diese Antwort?
Maciej Jureczko