Laden...

Entity Framework auf Änderungen überprüfen

Letzter Beitrag vor 15 Jahren 11 Posts 5.689 Views
Entity Framework auf Änderungen überprüfen

verwendetes Datenbanksystem: Oracle 10.2

Hallo zusammen

Ich habe ein Programm geschrieben, welches Daten mittels Entity Framework lädt und in einem DataGridView darstellt.
Nun möchte ich, dass beim Beenden des Programms überprüft wird, ob etwas verändert wurde und dann nachfragt, ob diese Änderungen gespeichert werden sollen.

Ich habe mir gedacht, es könnte so etwas wie eine hasChanges-Methode haben. Leider gibt es diese aber nicht.
Weiss jemand wie ich dies überprüfen kann?

Stichwort ChangeTracking.

Change Tracking and Identity Resolution (Entity Framework) (und verlinkte Seiten dort).

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

Danke für die schnelle Antwort.

Leider habe ich mich vorher schon bei msdn erkundet und diese Seite entdeckt. Jedoch ohne, dass ich verstanden hätte wie ich nun mein Problem lösen kann.

RowState

Hallo chanderegg,

Einfache Objekte haben keinen RowState. Deshalb wissen sie nicht, ob sie geändert wurden, wie das z.B. bei DataRows der Fall ist. Das Change Tracking vom EF ist aufwändig und umständlich. Warum? Schau: http://yellow-rainbird.de/blogs/rainbird/archive/2009/01/09/habe-sehnsucht-nach-dem-rowstate.aspx

Es geht nichts über ein typisiertes DataSet! Zur Hölle mit dem Ganzen OR-Mapper Kram!

Rainbird, nur weil OR-Mapper prinzipbedingt keine Objektzustände speichern? Kommt mir ein bisschen so vor (auch dein Artikel), als versuche man, die gewohnten Eigenschaften bei einer neuen Technologie auf Gedeih und Verderb zu übertragen und gibt frustriert auf, weil das so natürlich nicht funktioniert...

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

RowState

Hallo LaTino,

aufgeben nicht unbedingt, aber an Deiner Aussage ist schon was dran. Der Mehraufwand, den man mit dem Entity Framework gegenüber ADO.NET ist bei verteilten Anwendungen enorm (Ich schreibe meistens verteilte Anwendungen). Auch für das Databinding muss ich in meinen Forms-Anwendungen dafür wesentlich mehr Code schreiben. Ich frage mich einfach wofür? Nur weil es neu und cool ist?

Der einzige Vorteil vom EF ist aus meiner Sicht die Unabhängigkeit vom RDBMs und EntitySQL. Da ich aber fast ausschließlich für SQL Server entwickle, ist der Punkt für eigentlich kaum relevant. Ich kann den Mehrauswand alleine schon betriebswirtschaftlich nicht verargumentieren. Für eine EF Anwendung brauche ich definitiv mehr Zeit, weil ich mehr Code schreiben muss, ohne dafür wirklich einen Vorteil zu haben. LINQ funktioniert auch mit DataSets.

Oder gibt es Sichtweise, die den Mehraufwand rechtfertigt? Ich lasse mich gerne eines Besseren belehren.

Mehraufwand?

Wo? Beim Erstellen des Modells ja wohl kaum...beim Zugriff? Dank Linq auch extrem flexibel und schnell...ich seh jetzt nicht wirklich einen Mehraufwand, eher im Gegenteil - ich habe die in der DB abgebildeten Daten sauber Objekten zugeordnet, muss mich nicht mehr um Beziehungen und Konsistenz kümmern, und kann einfach mit Objekten arbeiten, ohne mich um die Speicherung zu kümmern. Letzten Freitag ein EDM für einen PHP/MySQL-Webshop geschrieben, auf dessen DB zugegriffen werden soll (mal völlig abgesehen von MySql) - für den kompletten Shop 3-4 Stunden modellieren bei knapp 80 Tabellen halte ich nicht für Aufwand, ehrlich gesagt.

LaTino
(EDIT: der meiste Aufwand war der Tatsache geschuldet, dass das DB-Design mal sowas von...nunja, suboptimal war. Da EF keine Inkonsistenzen im DB-Design erlaubt, muss man das Modell also "perfekt" bauen und auf eine "unperfekte" Datenbank setzen. Das Aufspüren dieser Stellen ist das Schwierige. Der Rest ist Tipparbeit - sicher eine Stunde ging drauf fürs Umbenennen der Properties und das Setzen ihrer Typen. Nicht schwer, aber nervig.)

EDIT2: Databinding erfordert mehr Code? Kapier ich nicht...wo? Wieso?

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

Mehraufwand mit EF

Hallo LaTino,

das Datenmodell ist mit dem Visual Studio DataSet-Designer mindestens genauso schnell modelliert, wie mit dem Entity Designer. Die Vorgehensweise ist auch sehr ähnlich: Tabellen auswählen bzw. per Drag & Drop auf die Designerfläche ziehen. Fertig!
Da hat EF keinen Vorteil (die RDBMS-Unabhängigkeit habe ich ja bereits eingeräumt, aber das soll jetzt mal nicht die Rolle spielen).

Um z.B. eine datengestützte ASP.NET-Anwendung zu bauen, ist EF super Klasse. Da stimme ich Dir voll zu. Da läuft aber auch alles auf dem Webserver. Das Change Tracking kann seine Arbeit machen und mit LINQ ist alles flexibel. Soweit so gut.

Wenn ich nun aber meine Geschäftslogik als Komponenten/Dienste auf einem Applikationsserver laufen lasse und Windows.Forms-Clients übers Netzwerk darauf zugreifen, dann habe ich beträchtlichen Mehraufwand. Folgende Beispile sollten das deutlich machen:

1. Mehraufwand, um Objektlisten vom DataContext zu detachen/attachen
Damit ich Objektlisten z.B. via WCF an den Client senden kann, muss ich sie vom Kontext trennen (detach). Wann und wie genau das passiert, darum muss ich mich selber kümmern. Wenn geänderte Objektlisten vom Client zurück an den Server gesendet werden, muss der Server diese wieder an einen DataContext anhängen. Auch darum muss ich mich selber kümmern. Wäre es mit context.Attech(list) bzw. context.Detch(list) getan, würde ich ja nicht meckern, aber so einfach ist es leider nicht.
Entfernt der Client ein Objekt aus der Liste, ist es einfach weg. Wenn der Client nun seine abgeänderte Objektliste zur Validierung und Persistenz an die Geschäftskomponente auf dem Applikationsserver sendet, kann diese nicht feststellen, dass ein Datensatz gelöscht werden soll (Die Objektliste war ja detached und auf dem Client gibt es ja keinen DataContext, der sich um das Change Tracking hätte kümmern können). Also muss der Client eine zweite Objektliste pflegen, in welche er Objekte kopiert, die gelöscht werden sollen. Dann habe ich serverseitig zwei Objektlisten (Die eigentliche Objektliste und eine zusätzliche, die zu löschende Objekte enthält). Aus diesen zwei Listen muss ich nun per Hand den Kontext aufbauen. Das ist jede Menge Code, die ich hinschreiben muss. Welche Objekte geändert sind und welche neu, weiss ich aber trotzdem noch nicht.
Egal wie ich es drehe und wende: Ich muss für meinen Client das komplette Change Tracking nochmal implementieren. Das Änderungsprotokoll muss die Geschäftskomponenete dann wieder mühsam dem DataContext beibringen, damit dieser die Änderungen persistieren kann. Alleine schon deswegen programmiert man sich einen Wolf. Die einfachste Variante ist da noch, die Original-Objektliste zu kopieren und die veränderte mit dieser Abzugleichen. Das bedeutet aber nach Adam Riese aber den doppelten Arbeistsspeicherkonsum. Außerdem muss ich die Original-Kopie auch wieder übers Netzwerk schicken, was unnötig Netzwerklast verursacht.

2. Mehraufwand fürs DataBinding bei Windows.Forms
Auch da machen die gelöschten Datensätze Probleme. Wenn ich ein DataGridView gegen eine Objektliste gebunden habe, und lösche über das DataGridView einen Datensatz, ist er weg. Also müsste ich diverse Events abonnieren, um mein selbstgebasteltes Change Tracking anzuwerfen, damit die Löschung auch korrekt im Protokoll vermerkt wird. Wenn ich nun im Client meine Änderungen verwerfen will, muss ich wieder basteln, weil POCOs einfach nichts könen außer Properties lesen und schreiben.

Um es auf den Punkt zu bringen: Das Change Tracking vom EF ist bei N-Tier-Applikation wirkungslos, da der temporäre Status beim Client liegt, weshalb dort die Eingaben gemacht werden und auch dort das Change Tracking greifen müsste. Das kann es bedingt durch die Architektur aber nicht.

Deshalb ist das EF nicht schlecht. Aber es ist einfach überhaupt nicht geeignet, um N-Tier-Anwednungen zu bauen.

Alle Lösungsansätze im Web (hier zwei Beispiele: http://community.dynamics.com/blogs/cesardalatorre/comments/9584.aspx, http://msdn.microsoft.com/en-us/magazine/cc700340.aspx), machen folgendes:


public interface AddressService
{
    IList<Address> FindAddresses(string searchPattern);

    void UpdateAddress(Address address);

    void DeleteAddress(Address address);

    void InsertAddress(Address address);
}

Das ist aber für eine N-Tier-Applikation denkbar ungünstig. Für jedes einzelne Update, Insert und Delete einen Netzwerkaufruf mit Overhead, Rechteprüfung und Neuer Transaktion auszuführen ist gekleckert statt geklotzt. Spätestens wenn ich einen Auftrag mit 30 Position auf einen Rutsch (innerhalb einer Transaktion) einbuchen will, kränkelt diese Vorgehensweise schon. Ich kann nicht für jede Position InsertSalesOrderDetail aufrufen. Das ist zu lahm und geht schon deshalb nicht, weil die Geschäftskomponente auf dem Server statuslos sein muss.

Mit Typisierten DataSets würde das AddressService-Beispiel so aussehen:


public interface IAddressService
{
    ContactMgmtDataSet.AddressTable FindAddresses(string searchPattern);

    ContactMgmtDataSet.AddressTable SaveAddresses(ContactMgmtDataSet.AddressTable addressesToSave);
}

Die komplette Persistenzlösung mit Validierung und Abgleich, sieht für den Client-Entwickler so aus:


// Änderungen persistieren
_localDataSet.Addresses.Merge(serviceProxy.SaveAddresses(_localDataSet.Addresses);

Auch der Auftragsdienst wäre ganz easy:


public interface ISalesOrderProcessingService
{
    SalesOrderSearchDataSet.SearchResultDataTable FindOrdersByNumber(string searchPattern);

    SalesOrderSearchDataSet.SearchResultDataTable FindOrdersByCustomer(Guid customerID);

    SalesOrderDataSet GetSalesOrderDocument(Guid orderID);

    SalesOrderDataSet GetApprovedSalesOrderDocuments();

    SalesOrderDataSet SaveSalesOrders(SalesOrderDataSet documents);
}

Vielleicht habe ich auch ein wichtiges Feature beim EF übersehen, welches all meine Probleme lösen würde. Ich bin für Vorschläge offen.

Hallo LaTino,
Hallo Rainbird,

Ich habe bis heute noch keinen OR Mapper gesehen, der seinen Objekten einen Status verpassen konnte. Das führt dazu diese nicht mit verteilten Anwendungen verwendbar ist (Ich lasse mich gerne des besseren Belehren). Microsoft wird wahrscheinlich bei der nächsten .NET Framework Version dies dem Entity Framework beibringen. Bis dahin gibt es verschiedene Implementierungen für dieses Problem (habe diese nicht getestet):

Hallo zusammen

Danke für Eure Disskusion. Ich sehe, dass in diesem Bereich noch nachholbedarf besteht.

Ich möchte euch nun noch meine Lösung präsentieren:

Ich stelle die Objekte in einem DataGridView dar und lade dieses immer wieder mit neuen Daten. Damit ich die Änderungen verfolgen kann, schreibe ich die Werte in Strings. (wenn eine Zelle/Reihe angewählt wird). Danach vergleiche ich diese Werte mit den mit den Zellen und notiere diese falls es Änderungen gegeben hat.
Es ist nicht gerade eine schöne Lösung, da für jede Spalte ein String erstellt werden muss und die Daten bei jedem SelectionChanged-Event in diese Strings geladen werden müssen. Es ist aber einfach zu programmieren und gibt keinen grossen Mehraufwand.

Hallo chanderegg,

Wie schon erwähnt kann dieses Verhalten schon mit DataTable abgedeckt werden, da die via DataRowState und DataRowVersion wissen was der Stand ist. Bei EF wird diese Funktion erst in der nächsten Version anscheinend geben.