5 Stimmen

Wie kann ich dieses SelectMany einen Join verwenden lassen?

Angenommen, ich habe drei Tabellen (Customer, Orders, und OrderLines) in einem Linq To Sql-Modell, wo

Kunde -- Eins zu Viele -> Aufträge -- Eins zu Viele -> OrderLines

Wenn ich die

var customer = Customers.First();
var manyWay = from o in customer.CustomerOrders
              from l in o.OrderLines
              select l;

Ich sehe, dass eine Anfrage den Kunden erreicht, das macht Sinn. Dann sehe ich eine Abfrage für die Bestellungen des Kunden und dann eine einzelne Abfrage für jede Bestellung, um die Bestellzeilen zu erhalten, anstatt die beiden zu verbinden. Insgesamt n + 1 Abfragen (ohne die Ermittlung des Kunden)

Aber wenn ich die

var tableWay = from o in Orders
               from l in OrderLines
               where o.Customer == customer
               && l.Order == o
               select l;

Statt einer einzigen Abfrage für jede Bestellung, die die Bestellzeilen abruft, sehe ich dann eine einzige Abfrage, die die beiden Tabellen verbindet. Insgesamt 1 Abfrage (ohne die Abfrage des Kunden)

Ich würde die erste Linq-Abfrage vorziehen, da sie mir lesbarer erscheint, aber warum verbindet L2S die Tabellen nicht, wie ich es in der ersten Abfrage erwarten würde? Mit LINQPad sehe ich, dass die zweite Abfrage in ein SelectMany kompiliert wird, obwohl ich keine Änderung an der ersten Abfrage sehe, ich bin nicht sicher, ob das ein Hinweis auf ein Problem in meiner Abfrage ist.

2voto

Francisco Punkte 4074

Ich denke, der Schlüssel dazu ist

customer.CustomerOrders

Das ist ein EntitySet, nicht ein IQueryable, so dass Ihre erste Abfrage nicht direkt in eine SQL-Abfrage übersetzt werden kann. Stattdessen wird sie als viele Abfragen interpretiert, eine für jede Bestellung.

Das ist jedenfalls meine Vermutung.

1voto

acermate433s Punkte 2494

Wie wäre es damit:

Customers.First().CustomerOrders.SelectMany(item => item.OrderLines)

1voto

Tomas Jansson Punkte 21651

Ich bin mir nicht 100%ig sicher. Aber meine Vermutung ist, weil Sie die Beziehung nach unten durchlaufen, dass ist, wie die Abfrage aufgebaut ist, im Vergleich zu der zweiten Lösung, wo Sie tatsächlich zwei Sätze durch einen Wert verbinden.

1voto

Amy B Punkte 104656

Versuchen Sie diese Abfrage:

IQueryable<OrderLine> query =
  from c in myDataContext.customers.Take(1)
  from o in c.CustomerOrders
  from l in o.OrderLines
  select l;

Sie können zur Definition der Eigenschaft CustomerOrders gehen und sehen, wie sich die Eigenschaft verhält, wenn sie mit einer tatsächlichen Instanz verwendet wird. Wenn die Eigenschaft in einem Abfrageausdruck verwendet wird, hängt das Verhalten vom Abfrageanbieter ab - der Eigenschaftscode wird in diesem Fall normalerweise nicht ausgeführt.

Siehe auch 本答 die eine Methode demonstriert, die sich in einem Abfrageausdruck anders verhält, als wenn sie tatsächlich aufgerufen wird.

1voto

David Punkte 11523

Also nach Franciscos Antwort und dem Experimentieren mit LINQPad habe ich einen guten Workaround gefunden.

var lines = from c in Customers
            where c == customer
            from o in c.CustomerOrders
            from l in o.OrderLines
            select l;

Dadurch wird das EntitySet in einen Ausdruck gezwungen, den der Anbieter dann in die entsprechende Abfrage umwandelt. Die ersten beiden Zeilen sind der Schlüssel, indem die IQueryable und entonces Wenn Sie das EntitySet in das SelectMany einfügen, wird es zu einem Ausdruck. Dies gilt auch für die anderen Operatoren, wie Where, Select, etc.

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