Laden...

OpenAccess ORM in einer Anwendung mit mehreren Fenstern

Erstellt von MorphieX vor 11 Jahren Letzter Beitrag vor 11 Jahren 2.082 Views
M
MorphieX Themenstarter:in
184 Beiträge seit 2012
vor 11 Jahren
OpenAccess ORM in einer Anwendung mit mehreren Fenstern

verwendetes Datenbanksystem: Firebird, aber eigentlich egal, da gemappt wird

Hi,

ich bin gerade dabei, mir eine Kundenverwaltung zu schreiben. (WPF, Telerik OpenAccess ORM) zumindest habe ich sowas vor.

Vorab: das Programm soll folgendermaßen aussehen:

Ein Hauptfenster, in dem man nach Kunden suchen kann.
Es gibt dort also eine Textbox, in die man einen Suchbegriff eingibt. Anschließend erscheint die entsprechende Trefferliste. (GridControl von DevExpress)
Außerdem gibt es in diesem Fenster drei weitere Schaltflächen1.Beim Klick auf „Löschen“ soll der in der Liste markierte Kunde gelöscht werden. 1.Beim Klick auf „Anlegen“ soll ein weiteres Fenster erscheinen, in dem man diverse Informationen eingeben kann (Adresse, Kundennr,… alles Properties der Eintity) 1.Beim Klick auf „Bearbeiten“ soll dasselbe Fenster wie bei „Anlegen“ erscheinen, allerdings mit den vorhandenen Daten des in der Liste markierten Kunden.

In dem „Detail-View“, also dem Fenster, was sich beim Anlegen oder Bearbeiten öffnet, soll es folgende Funktionen geben:1.Beim Klick auf „Speichern“ wird der Kunde gespeichert 1.Beim Klick auf „Abbrechen“ werden die Änderungen verworfen

Wichtig:1.Das Detail-View soll nicht modal angezeigt werden! Es soll also möglich sein, gleich mehrere Kunden parallel geöffnet zu haben und auch zu bearbeiten. 1.Sobald ein Kunde gespeichert wird, sollen die aktualisierten Daten wieder im GridControl stehen (sofern die Liste nicht zwischenzeitlich durch eine andere Suche verändert wurde) 1.Der Speichern-Button soll nur aktiviert sein, wenn auch tatsächlich Änderungen vorhanden sind. Das könnte man über den OpenAccessContext ermitteln: context.HasChanges)

Die ganzen Funktionen sind als Commands implementiert.

Mein Problem besteht jetzt darin, dass ich noch nicht genau verstehe, wie man die Entitäten über mehrere Fenster hinweg verteilt, speichert und ggf. zurücksetzt.
Ich habe schon viel in der Doku zu OpenAccess gelesen und sonst nach diversen Tutorials gesucht, aber nichts passendes gefunden, was sich damit beschäftigt.

Mein bisheriger Ansatz:
Im ViewModel des Hauptfensters habe ich einen OpenAccessContext. Darüber rufe ich meine Trefferliste ab.

Beim Löschen wird der markierte Kunde über den Context gelöscht
context.Delete(SelectedItem);
context.SaveChanges();

Beim Hinzufügen öffne ich die neue Detail-View und übergebe dort den Context aus dem Hauptfenster.
DetailView details = new DetailView(context);

Beim Bearbeiten öffne ich das neue Detail-View und übergebe den Context aus dem Hauptfenster, sowie den zu bearbeitenden Kunden.
DetailView details = new DetailView(context, SelectedItem);

Im ViewModel der Detail-View habe ich jetzt also Zugriff auf den Context, sowie auf den zu bearbeitenden Kunden.

Soweit klappt es.

Ich könnte den Command für „Speichern“ jetzt abhängig vom context de –und aktivieren:
context.HasChanges();

Und beim Ausführen von „Speichern“ einfach context.SaveChanges(); ausführen.
Doch was ist, wenn ich gleichzeitig einen zweiten Kunden bearbeite? Dann werden diese Änderungen auch gleich mitgespeichert…

Oder wenn ich Änderungen eingebe (aber noch nicht speichere), wieder zurück in das Hauptfenster springe und dort einen Kunden lösche? Beim Löschen wird ja auch SaveChanges() ausgeführt. Die Änderungen wären damit also auch wieder gespeichert…

Das ist also nicht der richtige Weg.

Ich vermute, ich brauche für jeden Kunden einen eigenen Context.

Ich könnte also schon von vorherein alle Kunden vom Context lösen. (context.CreateDetachedCopy) und beim Bearbeiten einem eigenen Context zuweisen (AttachCopy)

Das heißt ich arbeite in der Detail-View nur noch mit Kopien der eigentlichen Daten. Das wiederum bedeutet aber, dass die Daten später nicht automatisch im Hauptfenster aktualisiert werden, wenn ich sie speichere (es ist ja eine andere Instanz)

Egal wie ich es versuche, es gibt immer irgendeinen Haken bei der Sache… Am besten wäre es, wenn meine Entitäten eine eigene Methode zum Speichern und Abbrechen hätten, ich also gar nicht auf ein Context angewiesen wäre.
Dann könnte ich das Objekt einfach so an das Detail-View übergeben und ggf. Save() zum Speichern oder Cancel() zum abbrechen aufrufen…

Vielleicht übersehe ich ja noch etwas (ich bin ja noch Anfänger) oder ist der Mapper von Telerik nicht das richtige für mich?
Ist mein Szenario denn so ungewöhnlich, dass ich dazu keine einfachen Beispiele finde? Oder suche ich falsch?
Habt ihr Tipps für mich, wie ich das besser machen kann?

Ich sitze da jetzt schon einige Wochen (!) dran, und komme leider nicht weiter.

Ich hoffe hier kann mir jemand helfen. Würde mich sehr freuen.

M
MorphieX Themenstarter:in
184 Beiträge seit 2012
vor 11 Jahren

*push* ich versteh es leider nicht

Wenn das Telerik ORM zu speziell ist: wie würde sowas denn im EntityFramework aussehen?

16.842 Beiträge seit 2008
vor 11 Jahren

Es gibt immer Haken an der Sache und das lässt sich auch nicht vermeiden.
Deswegen ist man Entwickler. Man probiert aus, entwickelt Pattern und sucht sich Lösungen. 👍

Normalerweise holt man sich eine Entity, kopiert die Werte in ein ViewModel und zeigt dieses an. Beim Speichern holt man sich das Entity erneut, überträgt die ViewModel-Daten in das Entity und wirft dann nen SaveChanges().
So ist das auch im Einklang mit dem MVVM Pattern von WPF. Eine Entity gehört in meinen Augen nicht direkt in die View - auch kein Kontext. Und vor allem teilt man sich keinen Kontext mit irgendwelchen anderen Instanzen oder Fenstern. Das brauchst Du auch gar nicht, wenn Du nur noch das ViewModel durch die Gegend schiebst.

Da man nie weiß, ob das Entity während der Bearbeitung bereits aktualisiert wurde kann man nun irgendeinen Lock-Mechanismus (mit Timeout) implementieren, oder man speichert sich irgendwo das letzte Änderungsdatum.

Ich halte ehrlich gesagt von kommerziellen ThirdParty-Mappern nicht so arg viel. EntityFramework oder NHibernate sind in meinen Augen die Maßstäbe.

Vielleicht wäre mal der einfache Einstieg in WPF und damit auch das Erlernen des MVVM Patterns nicht soooo schlecht.

106 Beiträge seit 2011
vor 11 Jahren

Hallo MorphieX,

wenn ich das ganze richtig verstanden habe, dann liegt dein Problem nicht beim ORM sondern im Anforderungsprofil. Dabei ist es unerheblich ob du OpenAccess, EF oder NHibernate nutzt, die handhabung sollte bei den meisten sehr ähnlich sein.

Das Detail-View soll nicht modal angezeigt werden! Es soll also möglich sein, gleich mehrere Kunden parallel geöffnet zu haben und auch zu bearbeiten.

Das ist, meiner Meinung nach, eine merkwürdige Anforderung. Sinniger wäre es die DetailView modal zu machen oder den Datensatz der gerade bearbeitet wird, in deiner Übersicht zu sperren, sobald er bearbeitet wird.
Du schreibst nicht ob es sich um ein Einzel- oder Mehr-Benutzer system handelt, aber ich geh mal davon aus, das mehrere Benutzer gleichzeitig auf die DS zugriff haben, somit musst dir eh überlegen wie du mit gleichzeitigen Edits auf den DS umgehen willst. Wobei wir dann schon wieder beim Anforderungsprofil sind.
Derjenige (Kunde, Projektmanager, etc....) der bestimmt dass das Fenster nicht model sein darf, muss dir auch eine Vorgabe machen, wie du mit parallelen Edits umzugehen hast. Sollen sie sich gegenseitig überschreiben oder blockieren oder, oder, oder.....
All das muss die Anforderung definieren.
Wenn du diesen Punkt geklärt hast, sollte die Umsetzung schon etwas leichter fallen.

Und beim Ausführen von „Speichern“ einfach context.SaveChanges(); ausführen.
Doch was ist, wenn ich gleichzeitig einen zweiten Kunden bearbeite? Dann werden diese Änderungen auch gleich mitgespeichert…

Wir arbeiten zwar auch mit Telerik (um welten besser als CR), aber mit OpenAccess bin ich nicht vertraut. Wir nutzen NHibernate und dort gibt es keinen Context, aber eine Session die sowas ähnliches zu sein scheint. Bei einer Session kann ich "Session.Save(entity)" aufrufen und nur diese Entität wird dann gespeichert. Das scheint, so wie du es schilderst, nicht möglich zu sein. Somit bleibt dir praktisch nichts anderes übrig als den zu bearbeitenden DS aus deinem context zu lösen.
Du könntest allerdings auch mit ViewModeln, bzw ohne Databinding arbeiten(Du scheinst ja Databinding zu nutzen, ansonsten sollte es für dich nicht relevant sein wenn jemand in der DetailView Informationen eines DS ändert und in einer anderen DetailView auf speichern drückt).

Also mein Vorschlag:
Wenn du mit Databinding arbeiten willst, dann solltest du die Entitäten vom context lösen und erst mit dem auslösen von speichern wieder an den context hängen.

Oder

Verzichte auf das Databinding und fülle alle Felder deiner DetailView von Hand und schreibe sie beim speichern wieder zurück auf die Entität.

MfG
Rabban

Ps:

Ich halte ehrlich gesagt von kommerziellen ThirdParty-Mappern nicht so arg viel. EntityFramework oder NHibernate sind in meinen Augen die Maßstäbe.

OpenAccess ist der kostenlose ORM von Telerik, damit man "einfacher" mit deren Komponenten interagieren kann. Aber du hast recht, EF und NHibernate sind schon die Maßstäbe.

D
615 Beiträge seit 2009
vor 11 Jahren

OpenAccess ist der kostenlose ORM von Telerik, damit man "einfacher" mit deren Komponenten interagieren kann. Aber du hast recht, EF und NHibernate sind schon die Maßstäbe.

=> OpenAccess kann sich absolut zeigen lassen
=> Hatt einige Vorteile gegenüber dem EF

EF 4.0 vs Open Access ORM

Beste Grüsse

Diräkt

Edit:
@Rabban ; du kannst die Controls genau so gut und einfach verwenden mit einer anderen DataSource(xml,sql....)und auch egal ob mit dem EF, Nhibernate oder was auch immer...

M
MorphieX Themenstarter:in
184 Beiträge seit 2012
vor 11 Jahren

Vielen Dank schon mal für eure Antworten.

Das ist, meiner Meinung nach, eine merkwürdige Anforderung. Sinniger wäre es die DetailView modal zu machen oder den Datensatz der gerade bearbeitet wird, in deiner Übersicht zu sperren, sobald er bearbeitet wird.

Naja, es soll halt möglich sein, dass man mehrere Kunden parallel geöffnet haben kann.
So wie es in Word möglich ist, mehrere Dokumente parallel geöffnet zu haben. Wenn ich da bei einem Dokument auf Speichern klicke, werden ja nicht alle anderen Dokumente automatisch mitgespeichert.

Du schreibst nicht ob es sich um ein Einzel- oder Mehr-Benutzer system handelt, aber ich geh mal davon aus, das mehrere Benutzer gleichzeitig auf die DS zugriff haben, somit musst dir eh überlegen wie du mit gleichzeitigen Edits auf den DS umgehen willst. Wobei wir dann schon wieder beim Anforderungsprofil sind.

Also vorerst möchte ich keine bestimmte Locking-Variante einbauen. Der Datensatz wird immer überschrieben. Wer zuletzt speichert, hat die aktuellen Daten. Es kommt in der Praxis sehr selten vor, dass mehrere Benutzer im selben Kunden zur gleichen Zeit Änderungen vornehmen.

Somit bleibt dir praktisch nichts anderes übrig als den zu bearbeitenden DS aus deinem context zu lösen.
Du könntest allerdings auch mit ViewModeln, bzw ohne Databinding arbeiten(Du scheinst ja Databinding zu nutzen, ansonsten sollte es für dich nicht relevant sein wenn jemand in der DetailView Informationen eines DS ändert und in einer anderen DetailView auf speichern drückt).

Ja, ich binde die Entities über das ViewModel (ich habe im ViewModel also ein Property, das die ausgewählte Entität darstellt) an die View.
Ich dachte immer, das sei so richtig, da in fast allen Beispielen zu EntityFramework oder generell Mappern das so gemacht wird. Es hat auch den Vorteil, dass eine Anwendung schnell und einfach erweiterbar ist, wenn mal eine weitere Eigenschaft zu einer Entity hinzukommt.

Die Eigenschaften der Entity im ViewModel nochmal zu implementieren habe ich noch in keinem Beispiel gesehen.

Also mein Vorschlag:
Wenn du mit Databinding arbeiten willst, dann solltest du die Entitäten vom context lösen und erst mit dem auslösen von speichern wieder an den context hängen.

So versuche ich es mal...

Ich halte ehrlich gesagt von kommerziellen ThirdParty-Mappern nicht so arg viel. EntityFramework oder NHibernate sind in meinen Augen die Maßstäbe.

Noch kann ich den Mapper ohne weiteres ändern, das sollte also nicht das Problem sein.

106 Beiträge seit 2011
vor 11 Jahren

Ja, ich binde die Entities über das ViewModel (ich habe im ViewModel also ein Property, das die ausgewählte Entität darstellt) an die View.
Ich dachte immer, das sei so richtig, da in fast allen Beispielen zu EntityFramework oder generell Mappern das so gemacht wird. Es hat auch den Vorteil, dass eine Anwendung schnell und einfach erweiterbar ist, wenn mal eine weitere Eigenschaft zu einer Entity hinzukommt.

Ich weiß das viele Tutorials, gerade auch auf den MS seiten, mit diesen ViewModeln arbeitet, aber dies ist aus meiner Sicht in vielen Belangen eher Kontraproduktiv.

Die Eigenschaften der Entity im ViewModel nochmal zu implementieren habe ich noch in keinem Beispiel gesehen.

Dies wäre aber der richtige weg. Wir arbeiten bei uns mit View- und Edit-Modeln, in diese Models werden wirklich nur die Properties implementiert, die für diese View oder das Edit benötigt werden.
Das macht es erheblich einfacher die Models auch in anderen Projekten (z.B. einer Website) wieder zu verwenden. Also in Richtung Erweiterbarkeit eine großes Plus. Um dir die Schreibarbeit zu erleichtern, kannst du auch mal einen Blick auf AutoMapper werfen, der nimmt dir ne Menge arbeit ab.

Ein kurzes Beispiel um die Problematik mal zu erläutern:
Wenn du in einem Webprojekt die Entität direkt an die Edit-View gibst, dann werden dort aber bestimmt nicht alle Felder der Entität dargestellt (z.B. die ID, da die normalerweise für den Benutzer völlig irrelevant ist). Wenn also die Entität nach dem Bearbeiten wieder zurück kommt, so bekommt man nur die Felder zurück, die auch in der View waren, der Rest ist leer. Wenn man also diese Entität abspeichert, so speichert man nur die angezeigten(bzw. in der View versteckten) Daten, alles andere was für dieses Edit irrelevant war, fällt einfach unter den Tisch und wird mit den Leerdaten überschrieben.

Mir ist bewusst, das dies atm nicht auf dich zutrifft, aber wenn die Anforderung kommt, darfst du entweder alles nochmal neu schreiben, oder massiv Code doppeln, denn dort wirst du das von mir geschilderte Problem auf jeden fall haben, wenn du keine ViewModel nutzt.

du kannst die Controls genau so gut und einfach verwenden mit einer anderen DataSource(xml,sql....)und auch egal ob mit dem EF, Nhibernate oder was auch immer...

Ich weiß, aber das scheint bedeutend umständlicher zu sein, als wenn man es direkt mit OpenAccess macht. Ist ja auch irgendwie klar, es soll sich ja wie aus einem Guss anfühlen.

MfG
Rabban

D
615 Beiträge seit 2009
vor 11 Jahren

Hallo Rabban

Ich weiß, aber das scheint bedeutend umständlicher zu sein, als wenn man es direkt mit OpenAccess macht. Ist ja auch irgendwie klar, es soll sich ja wie aus einem Guss anfühlen.

Kann ich absolut nicht bestätigen. Arbeite seit knapp 3 Jahren mit Telerik Controls und habe nur für ein mini Test-Projekt mal Open Access benutzt...

Beste Grüsse

Diräkt

M
MorphieX Themenstarter:in
184 Beiträge seit 2012
vor 11 Jahren

Da scheine ich ja noch viel lernen zu müssen...
Kennt ihr ein gutes Tutorial / Beispiel, was in etwa meiner Problemstellung entspricht? Von mir aus auch Entity Framework, ich bin da offen...

Also WPF, MVVM, Entity Framework und das über mehrere Fenster...
Gibts da was? Die Sachen, die ich bisher gelesen habe, bringen mich da nicht weiter.

106 Beiträge seit 2011
vor 11 Jahren

Grundsätzlich scheint deine Herangehensweise schon nicht verkehrt, du musst nur zwischen Entität holen und Darstellung eine weitere Ebene einführen, die aus der Entität ein Model macht und aus dem Model wieder Entität.

Ich würde das ganze in 3 Ebenen unterteilen:
Repositories - für jede Art von Entität sollte es ein Repository geben(Ich weiß, die sinnigkeit von Repositories ist heiss diskutiert, aber ich bin ein Freund davon und kann sie nur weiter empfehlen). Also ein KundenRepository, ein ProduktRepository, etc.... Das Repository kümmert sich in erster Linie ums Laden, Speichern, Löschen und gegebenenfall Validieren der Entität. Damit kann man sicher stellen, das der Kunde immer gleich gespeichert und abgeprüft wird, ob auch alle relevanten Felder befüllt sind. (Es gibt noch mehr Anwendungsgründe für Repos, aber die solltest du selber nachlesen wenn es dich interessiert)

Tasks - und wieder gibt es für jede Art von Entität Tasks, also KundenTasks, etc....
Die Tasks erhalten die Aufgabe Anfragen aus der View entgegen zu nehmen und in View- bzw. EditModels zu verwandeln und EditModels entgegen nehmen und daraus wieder eine richige Entität zu machen und diese zu speichern. Die Tasks sind dabei die einzigen die mit dem Repositories kommunizieren, da meine Meinung nach, die Entitäten in einer View nichts zu suchen haben.

View - Die Darstellungsschicht, die Anfragen an die Tasks richtet alá GetCustomerModelByID(long customerId) und ein Model zurück bekommt.

Somit sind alle 3 Aufgabenbereiche klar von einander abgetrennt.
Ich hoffe das reicht dir erstmal als Einstieg.

MfG
Rabban

Ps: ein Tutorial habe ich leider nicht zur Hand, da dies auf Erfahrungswerten beruht.