none
[Entity Framework] Mehrere Tabellen in wenig Abfragen vereinen RRS feed

  • Allgemeine Diskussion

  • Hallo zusammen,

    ich habe derzeit ein Schulprojekt am laufen und zerbreche mir derzeit den Kopf an einer recht simplen Aufgabe. Dazu erst einmal ein Bild der Datenbank:

    In SQL und PHP würde ich sicherlich 3 Abfragen nutzen die durch Joins alle Tabellen verwenden. In EF gestaltet sich das für mich bisher noch um einiges schwerer. Arbeite noch nicht so sehr lang damit.

    Mein bisheriger Ansatz ist folgender:

    Order o = db.Orders.Include(z => z.CustomerDeliveryAddress.City).Include(z => z.CustomerInvoiceAddress).Include(z => z.Customer).Include(z => z.OrderProducts).First();

    Somit sind die Adressen, wie auch die Bestellung an sich abgehakt, die Benutzerinformation bekomme ich leider nicht unter. Der Debugger meldet immer das ich kein Last/Where verwenden darf.

    Mein nächster Schritt wäre von Produktvariante ausgehend auf Bestellte Produkte zuzugreifen, auch hier stellt sich wieder das Problem mit dem Where und Last.

    Der letzte Schritt wäre ausgehend der Variante den Preis anhand der Benutzergruppe zu ermitteln.

    Mit LazyLoading wäre das ganze recht simpel umzusetzen, dem entsprechend würden aber auch sehr viele Anfragen an den Server laufen.

    Habt Ihr eine Idee wie ich das lösen könnte bzw. etwas weiter optimieren? Über jede Hilfe bin ich dankbar.

    PS: An der Datenbank darf nichts mehr geändert werden.

    Viele Grüße

    A.Kuller


    • Bearbeitet A.Kuller Donnerstag, 26. Februar 2015 17:19 Ergänzung
    • Typ geändert Aleksander Chalabashiev Montag, 23. März 2015 10:17 keine bestätigte Lösung
    Donnerstag, 26. Februar 2015 17:17

Alle Antworten

  • Hallo,
    welche Daten willst du denn genau abfragen? Das konnte ich leider nicht mit Sicherheit heraus lesen. Was wohl auch helfen würde ist, wenn du uns einfach mal den SQL Code zeigst, den du in PHP nutzen würdest.

    Übrigens kennt auch LINQ eine SQL Artige Syntax. Unter anderem auch mit Join etc.:

    //Ein Beispiel aus der MSDN
    var innerJoinQuery = from category in categories join prod in products on category.ID equals prod.CategoryID select new { ProductName = prod.Name, Category = category.Name }; //produces flat sequence

    Die Möglichkeiten sind eventuell nicht überall so reichhaltig mit Schlüsselwörtern abgedeckt, jedoch gibt es in .NET auch die Möglichkeit noch eigene Methoden einzubringen. Daher sollte das kein Problem sein.

    Das EF versucht überall möglichst effizient zu sein. Ob LazyLoading wirklich so schlecht ist würde ich daher nicht pauschal entscheiden sondern es erst ein mal testen. Hin- und wieder ist es auch so, dass man zu kompliziert denkt und das EF es besser macht als man denkt oder auch genau anders herum. (Liegt an der Kompatibilität zu normalen SQL.)


    Tom Lambert - .NET (C#) MVP
    Wozu Antworten markieren und für Beiträge abstimmen? Klicke hier.
    Nützliche Links: .NET Quellcode | C# ↔ VB.NET Konverter | Account bestätigen (Verify Your Account)
    Ich: Webseite | Code Beispiele | Facebook | Twitter | Snippets

    Donnerstag, 26. Februar 2015 17:52
    Moderator
  • Hallo A.,

    aus Deiner Frage geht nicht ganz hervor, was am Ende herauskommen soll...

    Generell gilt, das bei Include (wie bei anderen Erweiterungsmethoden) ein vollständiger Ausdruck stehen kann, inkl. Where, Select etc. und Includes geschachtelt werden können, siehe Entity Framework .Include

    Als eine Einführung schau Dir mal an: Loading Related Entities, dort wird die grundlegende Syntax mit einigen Varianten gezeigt.

    Gruß Elmar

    Donnerstag, 26. Februar 2015 18:03
    Beantworter
  • Hallo zusammen,

    danke für eure Antworten.

    An sich benötige ich alle Daten die zu sehen sind. Spezifiziert bedeutet das:

    Produktvariante: Produktid, Name, Beschreibung

    Produktpreis: Preis in Abhängigkeit der Gruppe und der Anzahl

    Bestellte Produkte: Anzahl

    Bestellung: Id, Datum

    Adresse: Alles für Lieferung wie auch Rechnung

    Kundeninfo: Name, Kundennummer, Mail und Telefon

    Leider habe ich eben keine Testumgebung parat, würde aber die Querys folgendermaßen bauen:

    1. Select * From Order As o Inner Join CustomerAddress cd On o.deliveryid = cd.id Inner Join CustomerAddress As ca On o.invoiceid = ca.id Inner Join Customer As c On c.id = o.customerid Inner Join CustomerInformation As ci On c.id = ci.customerid Where ci.dateend >= o.date And ci.datestart <= o.date

    2. Select * From CustomerGroup Where CustomerId = (customer aus 1) And DateEnd >= (Bestelldatum aus 1) And DateStart <= (Bestelldatum aus 1)

    3. Select * From OrderProduct As op Inner Join ProductVariant As pv On pv.id = op.productvariantid Inner Join ProductPrice As pp On pp.productvariantid = pv.id Where op.orderid = (Bestellnummer aus 1) And pp.datestart <= (Bestelldatum aus 1)  And pp.enddate >= (Bestelldatum aus 1)  And pp.Groupid In (Gruppenid's aus 2)

    Sollte so ungefähr funktionieren, an sich werden alle Daten abgerufen in 3 Querys.

    Linq und Sql Syntax, das habe ich auch schon gesehen, wollte es aber gerne in der Methodenform nutzen. Macht den Code aus meiner Sicht übersichtlicher, an für sich sollte es ja das gleiche sein oder?

    LazyLoading, habe selbst das Problem gehabt das er viel zu viele Abfragen ausführte, bei einem Include sind aber auch oft Daten dabei die nicht benötigt werden. Z.B. andere Kundeninfos oder andere Preise. Das EF das ganze schon extrem vereinfacht habe ich bisher schon gemerkt, meine Anforderungen sind eben von 5 auf 50 gesprungen. Wenn ich das auch wieder verstanden habe sieht die Welt wieder anders aus.

    So etwas ist ja leider nicht möglich: ...Include(z => z.Customer.CustomerInformation.Last()) das hätte mir schon eine Menge Arbeit erleichtert.

    Viele Grüße

    A.Kuller


    Donnerstag, 26. Februar 2015 22:06
  • Hallo!

    Im Prinzip könnte man als Alternative zum LazyLoading auch die Alternative haben, auch einfach nur die ersten 100 oder 3000 Sätze einzulesen und sich den letzten Satz irgendwie zu merken, um später ab dem damals letzten Satz wieder 100 oder 3000 Sätze einzulesen. Intuitiv wäre das eher eine do- while Schleife als eine for- next Schleife, weil man sich ja nie sicher ist, dass es auch 100 bzw. 3000 Sätze gibt. Schon jetzt gibt es hier die Möglichkeit in SQL eine Zahl von Datensätzen zu überspringen und dann nur die Top 100 bzw. Top 3000 Sätze einzulesen. Was es aber nicht geben kann, ist, dass man im DataSet mit den Constraints einen Datensatz doppelt hat. Auch ist es nicht hilfreich, wenn man wegen der zwei Abfragen zu unterschiedlichen Zeiten dann einen Datensatz mittendrin doch nicht eingelesen hat. Bei dem letzten eingelesenen Datensatz ist es so, dass der später in der Datenbank doch nicht mehr da ist: Bei der Anzeige der offenen Bestellungen kann man ja zwischenzeitlich diese eine Bestellung abgeschlossen haben. Auch müsste SQL hierfür sich diesen letzten eingelesenen Datensatz so merken, dass man gleich an der Stelle mit der Abfrage weitermacht.

    Vielen Dank

    Klaus Kühl

    Sonntag, 26. Juli 2015 11:31
  • Hallo,
    joins mit dem Entity Framework kannst du mit LINQ machen: https://msdn.microsoft.com/en-us/library/bb311040.aspx

    Deine Datenquelle ist immer dein <Datenkontext>.<Tabelle>. Dann ist das fast wie in SQL!


    Viele Grüße Holger M. Rößler

    Mittwoch, 14. Oktober 2015 15:33