Autor Beitrag
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Di 24.06.14 15:52 
Servus,

ich habe mir etwas überlegt und wollte nach Meinungen fragen, bzw. ob es bereits etwas vergleichbares gibt.

Und zwar bekommt man aus den meisten ORMs jede Datenbank-Zeile raus und wenn die selbe Zeile an anderer Stelle erneut abgefragt wird, dann wird ein neues Objekt mit den gleichen Daten zurück gegeben.

Meine Idee ist, dass ich auf die reine Datenschicht eine neue Ebene setze, die dann nach Außen als Datenschicht sichtbar ist. Sie liest die Daten raus und hält intern Objekte für jede bereits abgerufene Datenzeile bereit, solange bis sie wieder geschlossen werden.

Wird die selbe Zeile erneut abgerufen, wird das selbe Objekt anhand der ID zurück gegeben.
Soll heißen, dass für jede Zeile einer Tabelle, die einen PrimaryKey hat, immer maximal ein Objekt im Speicher existiert.

Mein Vorgehen wäre so, dass ich diese Funktion in einer Basis-Klasse bereit stelle, wo dann auch die Werte der Properties gehalten werden. Die tatsächlichen Properties rufen sie dort ab oder schreiben sie dort. Dort kann ich dann auch dafür sorgen, dass eventuell mehrere Threads sich nicht ins Gehege kommen, ich kann dort INotifyPropertyChanged implementieren, ich kann dort den Schlüssel halten und mit dem Schlüssel auch Equals(object) implementieren.
Eine andere Möglichkeit für MultiThreading wäre, dass ein/e Datenzeile/Objekt für die Nutzung gesperrt wird, wenn ein anderer Thread oder User das Objekt gerade benötigt, bis es frei gegeben wurde. Das frei gegebene Objekt wird dann je nach Zustand mit der Datenbank abgeglichen und einen bestimmten Zeitraum bereit gehalten und anschließend raus geworfen, damit nicht zu viele Objekte auf ihre Nutzung warten. Ich bevorzuge die zweite Variante, da das für die Implementierung und Verwendung am einfachsten wäre.
Oder (als dritte Variante) ich überlege mir eine Möglichkeit, wie ich einen Thread eindeutig bestimmen kann und biete jedem Thread einen eigenen Arbeits-Raum, wo er schreiben kann und am Ende werden die Daten zusammen gemerged.

Das hätte dann für die Schichten, die die Daten nutzen, keinen Unterschied, es würde wie ein Adapter wirken, der genauso aus sieht, wie die Datenschicht benötigt wird.
Jede Zeile/jedes Objekt einer Klasse kann von der jeweiligen Auflistung (Beispiel: Person, Persons) bekommen werden. Ohne diese extra Schicht würde da für jede Zeile ein neues Objekt und mit dieser Schicht würde immer das selbe Objekt zurück gegeben werden.

Vorteile erhoffe ich mir darin, dass ich mir keine Gedanken mehr machen muss, wenn ein und die selbe Datenzeile an mehreren Stellen genutzt wird.
Außerdem muss ich außerhalb der Datenschicht keine Schlüssel mehr kennen, da ich die an sich ja nicht brauche und das Objekt selber für jede Datenzeile eindeutig ist. Die Schlüssel wären dann auch nur innerhalb der Datenschicht sichtbar und nach Außen hin sind existieren wirklich nur die Daten, die auch verarbeitet werden.

Nachteile sehe ich bei der zweiten Variante, dass immer nur exakt ein Thread an einem Objekt arbeiten kann. Für das konkrete Projekt stellt das kein Problem dar, für spätere Objekte könnte das aber durchaus ins Gewicht fallen.


Meine Frage nun:
Was haltet ihr davon und gibt es vielleicht schon etwas Vergleichbares?


Grüße
Udontknow
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2596

Win7
D2006 WIN32, .NET (C#)
BeitragVerfasst: Di 24.06.14 16:10 
Hallo,

grundsätzlich würde ich nicht anfangen, einen eigenen OR-Mapper zu schreiben.

Bei Hibernate ist das Verhalten so wie von dir gewünscht: Du bekommst bei zwei unterschiedlichen Ladevorgängen dasselbe Objekt geliefert, sofern du für die Ladevorgänge dieselbe Session nutzt. Andere Session: Anderes Objekt.
Das macht ja auch Sinn, vielleicht willst du ja mal ein Objekt, das sich schon längere Zeit im Speicher befindet, mit dem momentanen Stand auf der Datenbank abgleichen. Wenn deine Zwischenschicht dann aber immer sagt "habe ich schon, ich liefere aus dem Cache", wirst du nie Differenzen feststellen... ;-)

Nur ganz kurz zum MultiThreading, dasist sowieso wieder eine ganze Hausnummer für sich, es gibt da mit WPF dann auch immer Probleme, wenn gebundene Objekte ihre Wertänderungen an WPF signalisieren, das muss z.B. immer im Dispatcher-Thread passieren...

Viele Grüße

Andreas
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Di 24.06.14 16:18 
Zitat:
ich habe mir etwas überlegt und wollte nach Meinungen fragen, bzw. ob es bereits etwas vergleichbares gibt.


Solche Fragen sollten immer damit anfangen warum mann das will bzw. welches Problem man lösen möchte. ORMs haben üblicherweise einen Context an dem auch ein Object Caching hängt. Einen solchen Context kann man dann beliebig erzeugen und zerstören und nur die die an diesem Context arbeiten teilen sich dann auch den Objekt Cache. Contexte kann man dann beliebig viele parallel mit beliebiger Threadzugehörigkeit haben.

Ob man eine Context Threadübergreifend benutzen soll bzw. sollte ist dann einen andere Frage. Die ich potentiell eher mit nein beantworten würde. Wen man das braucht vermute eine sehr ~spezielle~ Sorte von Problemem die man nicht allgemein zu lösen versuchen braucht da eben ~speziell~.
Palladin007 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Di 24.06.14 16:43 
@Udontknow:

Einen eigenen OR-Mapper würde ich natürlich nicht schreiben, daher das Stichwort Adapter.
Das Ding würde dann seinerseits eine Datenschicht fordern, die dann die geforderte Daten-Struktur plus einen Primärschlüssel anbieten muss.

Oder es enthält selber einen ORM, dann fällt die "interne" Datenschicht ganz weg, das würde ich dann bevorzugen.

@Ralf:

Ein konkretes Problem gibt es nicht, es war nur eine Idee und der Versuch zu verhindern, dass andere Nicht-Datenschichten sich mit möglichst wenig Problemen aus der Datenebene beschäftigen müssen.
Es soll im Prinzip wirklich nur Abfragen anpassen und bestätigen sein, der Rest wird komplett intern geregelt. Der Gedanke ist eher daraus entstanden, dass ich nicht die ID rum reichen möchte um eine Zeile zu definieren, sondern ein Objekt, das ohne ID eindeutig ist und die Zeile zuverlässig repräsentieren kann. Das kann ja auch ein Dummy-Objekt sein, das dann bei Bedarf die benötigten Daten abruft, oder die Daten werden in einem eigenen Thread abgerufen, damit das Objekt auch ohne Daten verwendet werden kann, sie aber schon unterwegs sind, wenn sie dann benötigt werden.

Wenn nun jede Zeile in einem Objekt immer nur einmal existieren darf, dann ist es nicht mehr möglich, dass eine Zeile an zwei Stellen bearbeitet wird und nun entschieden werden muss, welche den Zuschlag erhält. Oder ich kann über INotifyPropertyChanged erfahren, ob sich mein Objekt an anderer Stelle geändert hat und darauf entsprechend reagieren.

Du schreibst, dass ORMs in der Regel einen Context und einen daran hängenden Cache haben. Udontknow hat Hibernate erwähnt, hat auch das EntityModel einen derartigen Cache und wenn ja, wie kann ich damit manuell arbeiten?
Bisher besteht mein "Wissen" daraus, dass ich weiß, wie ich Daten abrufen, manipulieren und schreiben kann, mehr jedoch (noch) nicht.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Di 24.06.14 20:49 
Zitat:
Udontknow hat Hibernate erwähnt, hat auch das EntityModel einen derartigen Cache und wenn ja, wie kann ich damit manuell arbeiten?


Hinter dem ObjectContext von EF steckt natürlich auch ein entsprechender Cache. Der ist dann aber auch gleichzeitig für die Änderungsverfolgung (nicht jeder will ja den gleichen Zustand des Objektes sehen) und für das Management von Objektbeziehungen (nicht nur Objekte können sich ändern sondern auch die Beziehungen zwischen Objekten) zuständig.

Manuell damit arbeiten tut man eigentlich nicht bzw. habe ich noch nicht. Man ruft Objekte aus der DB ab. Ob die nun aus der tatsächlichen Quelle kommen oder doch nur aus dem Cache bzw. aus einem Objekt des Caches abgeleitet wurden ist da völlig transparent (aka Magie) für den Programmierer. Der muss sich darum nicht kümmern.

Zitat:
Das Ding würde dann seinerseits eine Datenschicht fordern, die dann die geforderte Daten-Struktur plus einen Primärschlüssel anbieten muss.


Ein Objekt aus der DB hat nicht zwingend einen PK. Du solltest davon ausgehen in vielen Fällen einen künstlich festlegen zu müssen. EF macht das zum Beispiel bei Views (die eben oft keine PK liefern) in dem alle nicht nullable Felder kombiniert werden.

Zitat:
Das kann ja auch ein Dummy-Objekt sein, das dann bei Bedarf die benötigten Daten abruft, oder die Daten werden in einem eigenen Thread abgerufen, damit das Objekt auch ohne Daten verwendet werden kann, sie aber schon unterwegs sind, wenn sie dann benötigt werden.


Deferred Loading ist ein nettes Feature. Aber wohl kaum mit POCO Objekten zu kombinieren. Wenn ich mich dazwischen entscheiden müsste würde ich echtes POCO vorziehen. Datenobjekte die zwingend irgendwelche Interfaces oder Basisklassen voraussetzen neigen nur zu Problemen. Man braucht deswegen dann z.B. für andere Anwendungsfälle dann oft noch ein anderes Modell. In aktuellen Anwendungen gibt es schon oft genug mehrere Modelle (ein ViewModel, ein Webservice Model etc.) da muß man nicht noch grundlos ein weiteres hinzufügen nur weil man eins braucht mit einer bestimmten Vererbungshierarchie. Deferred Loading wäre was für Objektlisten die Objekte erst später (oder im Hintergrund) laden aber eher weniger für den Inhalt einzelner Objekte. Auch wenn das für bestimmte Typen, der Blob mit dem Photo zum Kunden im Customer Objekt, schonmal nützlich wäre aber eher Einzelfälle sind.
Palladin007 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Fr 27.06.14 12:10 
Zitat:
Ein Objekt aus der DB hat nicht zwingend einen PK. Du solltest davon ausgehen in vielen Fällen einen künstlich festlegen zu müssen. EF macht das zum Beispiel bei Views (die eben oft keine PK liefern) in dem alle nicht nullable Felder kombiniert werden.


In so einem Fall hätte das nicht funktioniert, ich hätte auch nicht alle nicht nullable Felder kombiniert, weil ich nicht sicher weiß, ob das eindeutig ist.
Dann wäre pro Abfrage ein neues Objekt ausgegeben worden.

Zitat:
Hinter dem ObjectContext von EF steckt natürlich auch ein entsprechender Cache. Der ist dann aber auch gleichzeitig für die Änderungsverfolgung (nicht jeder will ja den gleichen Zustand des Objektes sehen) und für das Management von Objektbeziehungen (nicht nur Objekte können sich ändern sondern auch die Beziehungen zwischen Objekten) zuständig.

Manuell damit arbeiten tut man eigentlich nicht bzw. habe ich noch nicht. Man ruft Objekte aus der DB ab. Ob die nun aus der tatsächlichen Quelle kommen oder doch nur aus dem Cache bzw. aus einem Objekt des Caches abgeleitet wurden ist da völlig transparent (aka Magie) für den Programmierer. Der muss sich darum nicht kümmern.


Kann ich davon aus gehen, dass jede Datenbankverbindung einen eigenen Context erhält?
Dann wäre das pro Instanz in der Tat einfach zu nutzen, solange ich immer die gleiche Verbindung habe.

Denn wenn das so ist und da man mit den POCOS ja auch eine Basisklasse setzen kann (wenn es denn nötig sein sollte), dann fällt ja m Prinzip alles, was ich vor hatte, ins Entity-Modell.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Fr 27.06.14 12:31 
Zitat:
Kann ich davon aus gehen, dass jede Datenbankverbindung einen eigenen Context erhält?


Nein. Du arbeitest im EF mit Contexten nicht mit Datenbankverbindungen. Ob der Context für deine Befehle dann ein oder mehrere Verbindungen benutzt ist sein interna das dich erstmal als Nutzer von EF nicht kümmern braucht.
Palladin007 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Fr 27.06.14 18:22 
Achso, jetzt verstehe ich :D

Na dann danke ich dir für die viele Hilfe und setze mich mal an ein kleines Mini-Übungs-Projekt ^^