Entwickler-Ecke
Basistechnologien - dynamisches Nachladen von Eigenschaften?
MHA - Fr 11.03.11 11:19
Titel: dynamisches Nachladen von Eigenschaften?
Hallo zusammen!
Ich hab mal wieder ein Problem. Grob gesagt geht es um Anwendungsarchitektur.
Und zwar hab ich zum Beispiel für jede Tabelle einer DB eine Klasse (Bsp.: Person), welche alle Eigenschaften aber keinerlei Funktionalität bereitstellt.
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| class Person { public string Name { get; set; } ... public int LandID { get; set; } public string Land { get; set; } ... } |
Dann gibt es jeweils eine Klasse DataAdapter, welche Methoden zum laden, speichern, update usw. bereitstellt.
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| class PersonDataAdapter { public List<Person> GetAll() {...} public Person GetByName(string name) {...} ... public bool Save(Person) {...} ... } |
Nun zum Problem: Ich will aus Performance-Gründen nicht bei jedem laden eines Objektes alle Eigenschaften laden müssen, welche in anderen Tabellen stecken.
Also im Beipiel die Eigenschaft "Land", welche den Namen des Landes der Person beinhaltet, sollte erst dann ermittelt werden müssen, wenn auch wirklich darauf zugegriffen wird. In etwa so:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
| class Person { private int _landID; public int LandID { set { if (_landID != value) _strLand = string.Empty; _landID = value; }
get { return _landID; } }
private string _strLand; public string strLand { get { if (string.IsNullOrEmpty(_strLand) && (_landID >= 0)) _strLand = GetLandByID(_landID).Bezeichnung; return _strLand; } } } |
ABER "GetLandByID" kann und darf es in der Klasse "Person" nicht geben, da "Person" nix von der DB weiß und auch gar nicht wissen soll!
Wie also kann ich das lösen???
Ich hoffe ich konnte mein Problem einigermaßen verständlich erläutern!
Gruß
Marc
IsNull - Fr 11.03.11 12:42
Dein Problem ist das allzeit beliebte OR Mapping, was so viel bedeuted wie Relationale Daten (Deine DB) in OOP abzubilden, und die Daten zu Mappen.
Wenn du nach ORM suchst wirst du einige Grundsätzlichen Vorgehensweisen finden, für C# kurz zusammengefasst:
Nutzt du einen aktuellen SQL Server, kannst du das in .NET integrierte Entities Framework nutzen. Sind es ältere SQL Server oder Non MS Server werden diese vom Assistenten in VS nicht mehr unterstützt (zumnidest unter VS 2010), die Lösung ist dann ein OpenSource ORM Framework wie NHibernate.
http://community.jboss.org/wiki/NHibernateforNET
Egal welches Grundsystem du hast, ab jetzt kannst du LINQ/Lambda Expressions nutzen, und das Subsystem kümmert kümmert sich dann selber darum, wann Daten wirklich geholt werden müssen.
C#-Quelltext
1: 2: 3: 4: 5: 6:
| var customerManager = new CustomerManager();
var mycustomers = from c in customerManager.Customers select c;
var first = mycustomers.First(); |
Aufpassen muss man einfach, dass man sowas...
C#-Quelltext
1:
| var customerlist = mycustomers.ToList(); |
...vermeidet, wenn es nicht unbedingt nötig ist. Ein ToList() generiert dann wirklich alle Objekte, obwohl man auch hier mit Proxy Techniken ein sehr spätes Laden (just in time, wenn die einzelnen Properties der Objekte benötigt werden) erreichen kann.
So ein System würde ich aber nicht selber entwerfen sondern die oben genannten nutzen :)
Grüsse
MHA - Fr 11.03.11 13:05
Hallo & Danke IsNull!
Klar gehts um ORM. Ich arbeite mit Firebird als DBMS und hab mir meinen eigenen kleinen OR-Mapper gebaut - funktioniert so auch prima.
Zitat: |
Egal welches Grundsystem du hast, ab jetzt kannst du LINQ/Lambda Expressions nutzen, und das Subsystem kümmert kümmert sich dann selber darum, wann Daten wirklich geholt werden müssen. |
Ehrlich gesagt, versteh ich nicht, wie mir LINQ bei meinem Problem helfen soll?
Mir gehts doch darum, das es Eigenschaften in meinen Datenklassen gibt, welche sich entweder auf andere Datenklassen beziehen oder sogar Auflistungen von Instanzen anderer Datenklassen sein könnten. Und diese Eigenschaften sollten nur dann "gefüllt" werden, wenn sie auch wirklich ausgelesen werden!
Also auf mein Beispiel bezogen: jede "Person" hält eine ID eines Objektes "Land". Außerdem soll es aber eine Eigenschaft "strLand" geben, welche erst dann befüllt (das ist ja hier wieder eine DB-Operation für sich) werden soll, wenn auf "Person.strLand" zugegriffen wird.
Gruß
Marc
IsNull - Fr 11.03.11 13:54
Nun, dann würde ich mich mal mit dem Proxy Pattern vertraut machen. ;)
Du hast dann deine echte Klasse "Land", sowie ein ProxyObjekt "LandProxy".
Die Proxyklasse muss das selbe Interface haben wie dein echtes Objekt (also entweder separates Interface definieren, oder der Proxy erbt und überschreibt von echten Objekt.)
Funktionieren tut es dann ungefähr so:
Wenn du eine Liste mit deinen Ländern willst, kriegst du diese in Form von ProxyObjekten zurück. Wenn du nun von so einem Objekt auf ein Property - sagen wir mal Land.Grösse zugreiffen willst, geschieht in der Proxy-Klasse folgendes: Die ProxyKlasse instanziert ein echtes Land-Objekt (d.h. DB Zugriff), und leitet den get/set Acessor auf das echte Objekt weiter. Selbstredent passiert das pro Proxyobjekt nur einmal, danach werden alle Zugriffe auf die interne Instanz weiter geleitet.
Von aussen merkst du von alldem nichts, du bist immer im Kontakt mit den Proxies und diese erstellen wenn nötig intern die echten Instanzen.
War das verständlich? :D
Ralf Jansen - Fr 11.03.11 13:59
Ich würde auch eher dazu raten einen etablierten ORM zu verwenden.
Wenn du es unbedingt selbst machen willst würde ich um dein gewünschtes Verhalten zu erreichen folgendes tun.
Dem PersonenDataAdapter ein generisches Interface verpassen in dem auch die von dir benötigt Funktion (die hier genannte GetLandByID Methode) einen generischen Aufbau hat und aus deinen dynamisch zu ladenden Properties aufgerufen wird.
z.B.
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| public IDataAdapter DataAdapter { get; set }
public string strLand { get { if (string.IsNullOrEmpty(_strLand) && (LandID >= 0)) strLand = DataAdapter.GetValueByID<string>(LandID); return _strLand; } } |
Das füllen eines geeigneten konkreten DataAdapters in eine konkrete Modellklasse würde ich dann über ein geeignetes
IoC [
http://de.wikipedia.org/wiki/Inversion_of_Control] Verfahren lösen.
Als zweiten Schritt könnte man dann den benötigten Code der jetzt noch im Property getter steckt in einen Aspect auszulagern. Z.B via
PostSharp [
http://www.sharpcrafters.com/]. In dem Aspect könnte man dann auch das ran holen des passenden DataAdapter lösen.
MHA - Fr 11.03.11 14:50
Vielen Dank euch zweien!
Auf den ersten Blick scheint das Proxy-Muster eine Lösung zu sein.
Ich werd mich mal ein wenig damit und mit IoC beschäfftigen.
Also nochmals vielen Dank & ein schönes Wochenende!
Gruß
Marc
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!