Autor Beitrag
MHA
Hält's aus hier
Beiträge: 15



BeitragVerfasst: Fr 11.03.11 11:19 
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.

ausblenden 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.

ausblenden 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:

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 97
Erhaltene Danke: 11


VS 2010, C#, AHK
BeitragVerfasst: 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. 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.

ausblenden 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(); // Das Subsystem generiert erst hier eine SQL abfrage und holt nur TOP 1 also den ersten Datensatz


Aufpassen muss man einfach, dass man sowas...
ausblenden 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 Threadstarter
Hält's aus hier
Beiträge: 15



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 97
Erhaltene Danke: 11


VS 2010, C#, AHK
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: 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.

ausblenden 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 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. In dem Aspect könnte man dann auch das ran holen des passenden DataAdapter lösen.
MHA Threadstarter
Hält's aus hier
Beiträge: 15



BeitragVerfasst: 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