CollectoinView nach Feld aus "child-Tabelle" filten.

  • WPF

Es gibt 15 Antworten in diesem Thema. Der letzte Beitrag () ist von Nofear23m.

    CollectoinView nach Feld aus "child-Tabelle" filten.

    Hallo,
    in meinem VS2015 Projekt lade ich die Daten aus
    zwei Tabellen mittels Entity Framework 6 über den context in meine ICollectionView.
    Es werden hier alle verfügbaren Daten ausnahmslos geladen.

    Das Prinzip wäre also folgendes:

    Meine_ICollectionView =
    CollectionViewSource.GetdefaultView(contex.Entity)

    Bei den zwei Tabellen aus der .mdf Datenbank
    handelt es sich einmal um Kunden-Daten, die zweite Tabelle enthält deren Aufträge.

    Für einen Kunden also mehrere Aufträge (1:N)

    Und nun möchte ich meine CollectionView gerne filtern.

    Das hatte ich mir folgendermaßen vorgestellt:

    Meine_ICollectionView.Filter = nur die Kunden bei
    denen mindestens noch 1 Auftrag als Erledigt = False eingetragen ist

    Wobei wie bereits erwähnt, die Kunden-Daten und
    die Daten der Aufträge aus 2 verschiedenen Tabellen der DB stammen.

    Kann mir bitte jemand mit der Syntax weiterhelfen?

    Hat vielleicht jemand ein Beispiel? Falls ja bitte
    in VB.NET.


    Vielen Dank im Voraus,
    Jeiss
    Hallo Jeiss

    Von wo die Daten kommen ist in diesem Fall im Grunde egal. Du verwendest Entity Framework und hast somit "Entitäten" also POCO Klassen.
    Je nachdem was du Datensätze in deinem ViewModel bearbeiten willst oder nicht empfielt es sich die POCO Klassen in ViewModel Klassen zu "wrappen" da die POCO Klassen ja nicht INotifyPropertyChanged implementieren.

    Aber so oder so hast du Klassen.
    Du hast Kunden und deren Aufträge. Wenn du auf die Aufträge der Kunden zugreifen willst musst du natürlich entweder per LazyLoading jeweils alle Aufträge der Kunden bei bedarf nachladen lassen wodurch aber immer beim Zugriff auf ein Property eine Abfrage an die DB geht oder z.b. per EagerLoading wodurch beim abrufen der Kunden ein JOIN über die Auftrags Tabelle generiert wird.

    Im Filter hast du dann die Daten zur Verfügung und kannst ganz normal darauf zugreifen.

    Aber sei gewarnt, solche vorhaben können performancetechnisch schnell mal in die Hose gehen. Hast du nur 100 Kunden mit je 10 Aufträgen sind das 1000 Datensätze die du da von der DB abrufst. Ob das so dolle ist will ich mal bezweifeln, aber das musst du natürlich selbst wissen.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    naja - er ruft ja alle Daten ab - dann lässt sich das Filter-Problem schon lösen (ob nu mit oder ohne ViewmodelWrapper).
    geht ja nur darum, einen geeigneten Predicate-Delegaten zu basteln, und dem ICollectionview zuzuweisen.
    Iwas wie

    VB.NET-Quellcode

    1. myCollectionview.Filter = Function(itm as Object) Directcast(itm, Kunde).Auftraege.Any(Function(x)Not x.Erledigt))

    ErfinderDesRades schrieb:

    er ruft ja alle Daten ab

    Das wissen wir nicht. Also zumindest ICH weis es nicht.

    Auch wenn der Kunde Aufträge hat ist die Navigationseigenschaft leer wenn LazyLoading abgeschaltet ist (was empfohlen wird). Und dann wird der Filter niemals etwas zurückgeben, deshalb wollte ich darauf hinweisen.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo,
    Ich danke euch beiden für eure schnelle Antworten.

    Also ich hab nicht alles verstanden, ist mir doch etwas zu "hoch". :|
    Zugegeben ich hatte, mangels technischem Fachwissens, enige Mühe mein Vorhaben und die aktuelle Laage genau zu beschreiben. Sorry!

    Tatsache ist aber, dass @ErfinderDesRades Lösungsvorschlag mein Problem zu lösen scheint. :thumbsup:

    Ist auch sehr gut "lesbar" und deshalb kann ich ja noch andere Filterkriterien, und sogar "Or" oder "And" anwenden.
    Ich will mich nicht zu früh freuen, muss das trotzdem noch ausgiebiger testen.
    Scheint aber genau das zu sein wonach ich suchte.
    Verstehe nicht warum ich beim googeln nichts gefunden habe. Lag womöglich auch an meinen "schlechten" Suchbegriffe...
    Aber egal, ich freue mich schon meine Icollectionview um einige Filtermöglichkeiten zu erweitern.

    Danke euch beiden,
    Jeiss

    Jeiss schrieb:

    Tatsache ist aber, dass @ErfinderDesRades Lösungsvorschlag mein Problem zu lösen scheint

    Dann ist LazyLoading eingeschaltet. Uiuiui :S
    OK, anderes Thema.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Jeiss schrieb:

    Ok, hab ich nicht bewusst so konzipiert...

    Ja, ist leider per Default im EF 6 so eingestellt, viele (ich auch) empfehlen aber Lazy Loading nur in bestimmten Fällen zu aktivieren in welchem man ganz genau weis was man tut.
    Siehe hier: levelup.gitconnected.com/3-way…net-core-app-d9b6295188cc
    Ist gleich der erste angesprochene Preformancerelevante Punkt. Solche erklärungen findest du zu hauf. In jedem Buch über EF wird das erwähnt und davon abgeraten.

    Jeiss schrieb:

    Mit welchen Nachteilen muss ich rechnen?

    Extrem schlechte performance, speziell wenn die DB mal an einem anderen Rechner oder sogar vieleicht über die WAN Leitung läuft.

    Stell dir vor du hast 1000 Kunden.
    In dem Moment wo du "Filterst" greifst du auf die Navigationseigenschaft von Kunden A zu. EF sagt "Hey ich habe noch keine Aufträge von Kunde a im RAM. Ich lade die Auträge mal schnell nach. Und eine Abfrage geht an die DB welche alle Aufträge inkl. allen Spalten der Auftragstabelle abruft.
    Das beim Filtern ja alle Elemente untersucht werden passiert das in diesem Moment aber für jeden einzelnen Kunden. Und zwar einzeln.
    Sprich.... du ruft in dem Moment wo du Filterst 1000 mal Daten von der DB ab anstatt 1x mit einem Join (Eager Loading).
    Bei größeren Tabellen geht dir da der SQL Server schnell mal in die Knie.

    Bei uns in der Firma sind die Clients z.b. so konfiguriert das in dem Fall mit den 1000 Kunden das schon reichen könnte das er dich rauskickt. Und schon ist der Client für ein paar Minuten tot und kann nicht mehr Arbeiten.

    Lass dich jetzt nicht zu sehr verunsichern, ich kenne weder deine Umgebung noch deine Datenbank, ich will dich nur darauf hinweisen, das ist eines DER Fallstricke von Entity Framework.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Ok,
    da bin ich aber beruhigt, dass das mit dem lazy loading nicht meine Schuld ist.... ;)
    Na ja Performance dürfte jetzt auf jeden Fall noch kein Thema sein.
    Bin aber froh, dass du mich drauf hingewiesen hast. Man kann ja nie genau wissen wie gross eine DB später einmal werden wird.
    Und außerdem, hatte ich das auch falsch verstanden min dem filtern.
    Ich lade die Daten, und zwar sämtliche gleich beim starten der Anwendung.(eine Desktopanwendung)
    Für meine Begriffe war das das einzig Mal wo auf die DB zugegriffen wird! Und dann wären sämtliche Daten irgendwo in der RAM.
    Und der Filter würde bloß einige dieser Datensätze irgendwie "auslesen".
    Hab nicht gedacht, dass dann erneut die DB gestresst wird...
    Ok, ich lese dann mal deinen Link zu dem Thema.

    Danke,
    Jeiss

    Jeiss schrieb:

    Ok, ich lese dann mal deinen Link zu dem Thema.

    Schau dir mal die Abfragen mit dem SQL Profiler an oder schalte den Log von EF ein. Da siehst du das alles genau. Kann ich nur empfehlen.

    Ich schau mir immer JEDE Abfrage genau an, man glaubt garnicht was das bringt. Unwarscheinlich.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Nofear23m schrieb:

    schalte den Log von EF ein
    Das geht so:

    VB.NET-Quellcode

    1. myObjectContext.Log = Console.Out
    Und meine annere Lösung ist tatsächlich nur sinnvoll, wenn du wirklich wie du sagst, alle Daten initial lädtst (was allerdings vonselbst nicht läuft, sondern das musste veranlassen).
    Wenn hingegen lazyloading an ist, dann wird mein FilterMethödchen aus post#3 für jeden Kunden, der dort abgeprüft wird, eine Sql-Query an die DB abschiessen, die alle seine Aufträge einholt - sehr resourcen-stressig.
    Aber genau das kannste ja dann im OutputFenster sehen - wennde das Ef-Logging wie gezeigt aktiviert hast.
    Hallo,
    also keine Ahnung wieso, aber um den Log von EF6 auszugeben, das geht bei mir anders...

    VB.NET-Quellcode

    1. myObjectContext.Log = Console.Out

    geht bei mir nicht. Also ich hab's nicht hingekriegt!
    Bei mir ist es eher sowas.

    VB.NET-Quellcode

    1. _context.Database.Log = AddressOf Console.Write

    Aber wie gesagt, fragt mich bloß nicht warum das so ist.
    Hab gegoogelt bis ich was hatte was bei mir ging.....

    Mit dem Output, weiß ich allerdings nicht sehr viel anzufangen.
    Außer, dass die Verbindung zur Daten Bank beim laden der Daten tatsächlich in 2 Sekunden 4 mal geöffnet und geschlossen wird.
    Beim setzen eines Filters passiert das 23 mal in 4 Sekunden?!?
    Ich kann aber nicht beurteilen ob das jetzt schlimm ist oder nicht?!?

    Ich lass das jetzt einfach mal so sein und konzentriere mich auf die Funktionalität meiner kleinen App.
    Und wenn ich dann später mal ein Performance Problem kriegen sollte, dann sehe ich weiter.

    Außerdem muss ich sowieso vielleicht die SQL Datenbank gegen eine SQLite Datenbank austauschen.... Aber das ist auch wiederum eine andere Geschichte....

    Auf jeden Fall danke ich euch, dass ihr mir wieder ein kleines Stückchen in meiner Anwendung weiter geholfen habt.

    Danke,
    Jeiss

    Jeiss schrieb:

    Beim setzen eines Filters passiert das 23 mal in 4 Sekunden?!?
    Ich kann aber nicht beurteilen ob das jetzt schlimm ist oder nicht?!?


    Jo, dassis ziemlich k...cke.
    Weil prinzipiell sollten diese Daten einmal geholt werden und gut ist.
    Und man kann EF auch so konfigurieren, dasses das bei diesen beiden Tabellen so macht.
    Aber ich bin in EF nicht viel unterwegs...
    Gebe ich @ErfinderDesRades recht, das ist nicht gut, und hier hast du vermutlich nur ein paar Testdaten in der DB. In einer "Real-World" Anwendung hast du hier schnell mal echte probleme am Hals.

    ErfinderDesRades schrieb:

    Und man kann EF auch so konfigurieren, dasses das bei diesen beiden Tabellen so macht.

    Beim Abrufen der Kunden einfach mittels Include(Function(x) x.Auftraege) diese mitladen.
    Wobei -- auch hier vorsicht. Hat man eine Menge Kunden mit dementsprechneden Auftr#gen kann das schon eine flut an Daten bedeuten. Aber das muss man eben von Fall zu Fall beurteilen.

    Prinzipiell kann ich jedem der mit EF unterwegs ist nur empfehlen sich das ganze genauer anzusehen. EF ist sehr mächtig in der richtigen Anwendung, kann aber sehr schnell richtig in die Hose gehen.

    Noch ein Performancetip der enorm viel rausholt. Wenn die Daten im View nicht bearbeitet werden müssen rufe die Daten immer mit NoTracking() ab. Damit spart man sich den Overhead des ChangeTrackers.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hallo,
    Include ist ja doch vielleicht gar nicht so schlecht.....?!
    Hab meine query geändert, und wenn ich dem output log glauben kann sind wir jetzt nur noch bei 4x connection öffnen/schließen in 2 Sekunden beim laden der Daten.
    Und filtern ist jetzt komplett Kostenlos. Die DB wird gar nicht mehr geöffnet. Kann das sein?
    Das müsste doch eigentlich weniger Stress für die Anwendung bedeuten....?

    Da kann ich euch ja nur danken , dass ihr so "hartnäckig" ward ;)

    Danke,
    Jeiss
    Das ist richtig.
    Beim abrufen mit Include() der Kunden wird ein Join über die Auftragstabelle gemacht. Und somit alle Daten mit nur einem Query abgerufen.

    Aber Vorsicht. Bei 1000 Kunden mit je nur 10 Aufträgen sind das gleich mal 11000 Datensätze. Stell dir vor was bei 400 000 Kunden passiert. Da ruft er vermutlich mal 10 Sekunden ab. Wie gesagt, man muss sich das immer gut überlegen.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##