DBContext (EF6) - Status einer Entität (ändern)

  • C#

Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von MasterQ.

    DBContext (EF6) - Status einer Entität (ändern)

    Hallo,

    ich möchte an meine Frage von vor einer Woche anknüpfen. Irgendwie finde ich keine verständliche Beschreibung wie man Daten, ausgehend von einer Tabelle in einer Datenbank, mit Hilfe von EF6 und DataBinding in ein Formular und auch zurück bekommt. (Fast) Alles was so beschrieben ist, hilft mir nicht in der Praxis.

    Punkt 1)
    Nimmt man die Dokumentation für EF6, DataBindung, WPF oder auch WinForms, dann bekommt man Daten aus einer DB in einer Struktur DataGrid(View) angezeigt. Das ist kein Hexenwerk, in einer echten Anwendung allerdings meist nur die halbe Miete.

    Punkt 2)
    Hat man die Daten in einem DataGrid(View) kann man auch durch das Eingeben neuer Zeilen in der Tabelle, diese ohne große Umstände in die DB schreiben.

    Punkt3)
    Will man allerdings Datensätze löschen, dann machts aua!

    Punkt 4)
    Gleiches wie unter 3) gilt wenn vorhandene Datensätze inhaltlich geändert werden.


    Unter EF6 bietet der ChangeTracker des Contextes die Möglichkeit, verschiedene Aktionen an Datensätzen zu verfolgen und gibt dazu Angaben zum Status einer Entität. Diese wären "Added", "Modified", "Detached", "Deleted" und "Unchanged". Hilft einem das? Irgendwie nicht! Oder doch?

    Die Daten aus der DB werden typischerweise über DataBinding an ein Steuerelement gebunden. Das geht entweder über z.B. List, BindingList oder ObservableCollection. Egal wie, ich muss Events abfangen, die die Änderungen in der Tabelle behandeln und in den Datenkontext übernehmen.

    DBContext bietet mit der .Entry()-Methode Zugriff ein DbEntityEntry-Objekt und dieses auf den Status der angegebenen Entität. So zumindest beschreibt es die Dokumentation.

    Ruft den Zustand der Entität ab oder legt diesen fest.
    public System.Data.Entity.EntityState State { get; set; }


    Damit sollte man meinen, dass man mit einem

    C#-Quellcode

    1. ​dbContext.Entry(entität).State=EntityState.Deleted;


    den Status von entität auf "Gelöscht" setzen kann, damit bei dem nächsten Sync über

    C#-Quellcode

    1. ​dbContext.SaveChanges();
    das korrekte SQL-Statement abgeschickt werden kann. Aus dem Context sollte entität nicht gelöscht werden, denn dann würde das Pendant in der DB nicht angefasst werden und bliebe unverändert. Der Befehl löscht entität aber aus dem (lokalen) Context. Das ist doch Blödsinn, oder?

    Also ich kriege es hin, die Events des DataGrid, der Listen oder Collections abzufangen um auf die Aktionen wie neue Datensätze anlegen oder alte löschen zu reagieren. Ab wie bringe ich dem Context bei, die Statusangaben entsprechen zu setzen? Oder geht das ganz anders?

    Wo kann ich mal nachlesen, wie man die Aktionen Löschen und Modifizieren umsetzen kann?

    Danke

    MQ

    MasterQ schrieb:

    Punkt 1)
    Nimmt man die Dokumentation für EF6, DataBindung, WPF oder auch WinForms, dann bekommt man Daten aus einer DB in einer Struktur DataGrid(View) angezeigt. Das ist kein Hexenwerk, in einer echten Anwendung allerdings meist nur die halbe Miete.
    Schon hier grundfalsch.
    Man bekommt keine Daten aus einer DB in einer Struktur Datagrid(View) angezeigt.
    Man bekommt zunächstmal den Code für Datensatz-Klassen generiert.
    Wenn man zur Laufzeit Daten abruft bekommt man eine Liste solcher Daten-Objekte.

    MasterQ schrieb:

    Punkt 2)
    Hat man die Daten in einem DataGrid(View) kann man auch durch das Eingeben neuer Zeilen in der Tabelle, diese ohne große Umstände in die DB schreiben.
    Man hat keine Daten in einem DataGrid(View).
    Die Daten sind - s. Punkt1 - in einer Liste von Datensatz-Objekten.

    Punkt 3 + 4 scheinen mit obigem Missverständnis zusammenzuhängen - worauf du vmtl. etwas aufgebaut hast, was nicht funktioniert.
    Jedenfalls aus der Liste von Datensatz-Objekten kann man sehr wohl löschen, und die Datensätze darin kann man auch ändern.
    Wenn richtig gemacht, wird dbContext.Save() die Änderungen und Löschungen auch in die Db zurückschreiben.

    Allerdings im Detail kann ich nicht weiterhelfen, weil ich verwende die typisierten DataTables des typisiertes Dataset. Da funzt die Änderungs-Verfolgung, und ich fummel da auch nicht dran rum.

    Bei EF scheint das iwie komplizierter zu sein, oder EF-Programmierer machen es sich komplizierter als nötig.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ErfinderDesRades schrieb:

    ...
    Schon hier grundfalsch.

    Na ja. Ich habe etwas flapsig formuliert aber jeder weiß, was ich meine. Wenn man es mit den Formulierungen so genau nimmt, wie du es hier forderst, dann hat man auch keine Daten in einer Liste.

    Die Daten sind eigentlich in Instanzen von Klassentypen abgelegt und eine Liste ist nur ein Liste von Verweisen auf diese Instanzen. ...


    Punkt 3 + 4 scheinen mit obigem Missverständnis zusammenzuhängen - worauf du vmtl. etwas aufgebaut hast, was nicht funktioniert.
    Jedenfalls aus der Liste von Datensatz-Objekten kann man sehr wohl löschen, und die Datensätze darin kann man auch ändern.
    Da hast du mich falsch verstanden. Natürlich kann man aus diesen Listen Daten löschen oder diese ändern.

    Mein Frage lautet, wie ich diese Änderungen in der lokalen Liste, die als Datenquelle für das Steuerelement (DataGrid) dient, in die DB übertragen bekomme.


    Allerdings im Detail kann ich nicht weiterhelfen, weil ich verwende die typisierten DataTables des typisiertes Dataset.
    Schade.


    ...
    Und jetzt habe ich wahrscheinlich mein Problem erkannt. Ich muss über den Changetracker gehen, da bleiben die Entitäten mit dem Status "Deleted" drin.

    Jetzt verstehe ich auch folgendes Zitat:

    Nofear23m schrieb:

    ... Es wirkt für mich als würdest du den Context als solchen und den ChangeTracker in einen Topf werfen. ...


    Da hatte Nofear23m (Sascha) offensichtlich recht.

    Mea culpa

    Grüßle

    MQ
    Sorry,wenn ich dir formuliermässig Unrecht getan hab. Es gibt sehr sehr viele, die, was du flapsig formuliertetest, aber anders meintest, genau so gemeint hätten, wie du es formuliertetest.

    Aber zeigst du uns noch, wie du es nu gelöst hast? Täte mich interessieren, weil bin da wie gesagt unterbelichtet.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    zunächst möchte ich noch mal klarstellen, dass meine "Probleme" nicht daher rühren, dass WPF oder EF6 irgendwie kaputt wären sondern einzig und alleine daran, dass ich Sachen (Beschreibungen) falsch interpretiert hatte.

    Ich lese Daten von der DB und erzeuge eine BindingList als Datenquelle für mein Steuerelement.

    C#-Quellcode

    1. ​var züge = db.Züge.Where(xx => xx.PlanID == planID).OrderBy(xx => xx.Ordinal).ToList();
    2. Züge = new BindingList_fixed<ZugEntity>(züge);


    Aus den drei möglichen Varianten für die Art der Datenquelle (List, BindingList, ObservableCollection) habe ich mich für BindingList entschieden, da diese mir die besten Möglichkeiten zu bieten scheint. Nun ist BindingList allerdings bissl verbuggt, so dass ich eine Kleinigkeit korrigieren muss.

    C#-Quellcode

    1. public class BindingList_fixed<T> :BindingList<T> {
    2. public BindingList_fixed(IList<T> list) : base(list) { }
    3. public T ItemInMind { get; set; }
    4. protected override void RemoveItem(int index) {
    5. ItemInMind = this[index];
    6. try {
    7. base.RemoveItem(index);
    8. }
    9. catch (Exception ex) {
    10. Debug.Print(ex.Message);
    11. }
    12. }
    13. }



    BindingList bietet ein Event (ListChanged), das auf Änderungen in der Liste reagiert. Werden "Daten" in der Tabelle bearbeitet, gelöscht oder hinzugefügt, dann wird das in die BindingList übernommen und da das Event geworfen, das ich dann bearbeite. Dieses Event ist verbuggt (s.o.) weil innerhalb der Eventroutine das zu löschende Element nicht mehr zugänglich ist. Dieses benötige ich aber, um es aus dem Context der Datenbank db.Züge löschen zu können. Ich muss es mir also irgendwie merken.

    C#-Quellcode

    1. private void züge_ListChanged(object sender, ListChangedEventArgs e) {
    2. BindingList_fixed<ZugEntity> list = sender as BindingList_fixed<ZugEntity>;
    3. ZugEntity item;
    4. switch (e.ListChangedType) {
    5. case ListChangedType.ItemAdded:
    6. item = list[e.NewIndex];
    7. item.Ordinal = (e.NewIndex + 1) * 10;
    8. item.PlanID = planID;
    9. db.Züge.Add(item);
    10. break;
    11. case ListChangedType.ItemDeleted:
    12. item = list.ItemInMind;
    13. db.Züge.Remove(item);
    14. break;
    15. default:
    16. break;
    17. }
    18. }



    Zur Verwirrung (zumindest bei mir) trug bei, dass folgendes missverstanden wurde:

    Der Datenbankkontext ​DbContext bzw. dessen Instanz​db hier im Projekt enthält die ausgelesenen Daten in einer Klasse vom Typ ​DBSet, hier ​Züge. Daneben gibt es noch den ChangeTracker, Typ ​DBChangeTracker, der die Änderungen nachverfolgt und daraus beim Synchronisieren mit der Datenbank die Info gibt, ob für einen Datensatz INSERT, UPDATE, DELETE oder nichts anzuwenden ist.

    Beide Strukturen, sowohl db.Züge als auch db.ChangeTracker bieten kein vollständiges Abbild aller lokal vorhandenen Datensätze. Da gibt es noch mehr im Hintergrund. Beide Strukturen filtern, je nach dem wie der Status der Entität ist. Und genau das war mein Verständnisproblem. Ich hatte Datensätze erwartet die nicht da waren bzw. umgekehrt, je nachdem ob ich in db.Züge oder in db.ChangeTracker nachgeschaut hatte.

    Beispiel:
    Eine Entität mit dem Status Added findet man in beiden, in ​db.Züge als auch in ​db.ChangeTracker. Ändert man den Status mit ​db.Entry(entität).State=Detached und schaltet so die Nachverfolgung aus, erscheint entität nicht mehr im ChangeTracker, in Züge aber immer noch.

    Ändere ich den Status erneut ​db.Entry(entität).State=Deleted, dann verschwindet sie in Züge und taucht in ChangeTracker wieder auf ohne dass ich sonstwo noch was anderes tun müsste.


    Alle Klarheiten beseitigt?

    Gruß

    MQ
    thx - hübsch, die erweiterte BindingList. (ich würd das nicht bug nennen, ist ja keine Fehlfunktion, sondern ist ein Feature, was fehlt. (sorry, dassich schonwieder herumklugscheisser)).

    Ansonsten siehts mir wieder mal so aus, als sei das Change-Tracking bei EF unbrauchbar, und jeder Coder muss sich da einen eigenen Weg zurechtbasteln, um iwie doch in den Genuss dieser eigentlich doch Selbstverständlichkeit zu kommen.

    ErfinderDesRades schrieb:

    ... (sorry, dass ich schon wieder herumklugscheisser)).

    Nur zu! Wenn es dir gut tut! :thumbup:

    Ansonsten siehts mir wieder mal so aus, als sei das Change-Tracking bei EF unbrauchbar, und jeder Coder muss sich da einen eigenen Weg zurechtbasteln, um iwie doch in den Genuss dieser eigentlich doch Selbstverständlichkeit zu kommen.

    Wie immer, ist bei dem einen das, bei dem anderen was anderes besser gelöst. Unbrauchbar ist das ChangeTracking bei EF aber mit Sicherheit nicht.

    Bin mal gespannt, was bei dem n:m-Dinges raus kommt!

    Grüßle

    MQ