Laden...

Architektur: Wie Objekte, von denen nicht (gleich) alle Properties benötigt werden, effizient laden?

Erstellt von larsux vor 13 Jahren Letzter Beitrag vor 13 Jahren 3.835 Views
L
larsux Themenstarter:in
8 Beiträge seit 2010
vor 13 Jahren
Architektur: Wie Objekte, von denen nicht (gleich) alle Properties benötigt werden, effizient laden?

Hallo,

also ich hab mal eine grundsätzliche Frage zur OOP mit C# unter ASP.NET.

Situation:
Ich hab ein Webformular welches beim Aufruf Kundendaten von einem SQL-Server holt und dann in einer dynamisch generierten Tabelle ausgibt. Ich greife nur lesend auf diese Daten zu und benötige sie auch noch im späteren Verlauf in anderen Formularen der Webanwendung. Nun könnte ich ja jeden Kunden als Objekt anlegen und diese Objekte dann in einer generischen Liste ablegen und in der Session(Oder vllt. woanders[Cockies?]) speichern um später wieder auf die Daten zuzugreifen.

In dem Moment wo ich für jeden Kunden ein Objekt Kunde anlege würde ich ja sämtliche Kundendaten vom SQL - Server holen obwohl ich auf der Startseite nur Kundennummer, Name und Vorname anzeige. Die kompletten Daten benötige ich ja eigendlich erst wenn der Nutzer den Kunden auswählt um detailiertere Informationen zu Ihm anzuzeigen.

Würde ich es nicht objektorientiert machen, dann würde ich erst beim Aufrufen der Webanwendung über SQL nur die Kdnr, Name und Vorname abfragen und beim Klick auf den Kunden eine erneute Abfarge mit der Kundennummer durchführen um die detailierteren Infos zum Kunden zu bekommen.

So nun meine erste Frage. Welche Vorgehensweise ist euer Meinung nach am effektivsten/besten???

Wenn ich es objektorientiert löse dann würde ich doch die Methode getKunden(Ausgabe aller Kunden) mit in die Klasse Kunden aufnehmen.

public class C_Kunde {
private int _KndNr;
private string Name;
private string Vorname;
.....
public string KndNr {
   get { return _KndNr; }
   set { _KndNr = value; }
}
......
public List<C_Kunde> getKunden() {
  List<C_Kunde> Kundenliste = new List<C_Kunde>;
  sqlQuery = "SELECT * FROM Kunden;";
  .....
  while (sqlReader.Read) {
     C_Kunde Kunde = new C_Kunde();
     Kunde.KndNr = sqlReader.GetValue(0);
     .......
     Kundenliste.Add(Kunde);
  }
  return Kundenliste;
}
}

Ist mein Denkansatz korrekt oder eher nicht so?

Vielen Dank...

Gruß larsux ;D

Gruß larsux....

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo larsux,

eigentlich geht es dir ja um Delayed Loading. Du willst die Daten erst laden, wenn du sie braucht. Mal abgesehen davon, dass das bei heutigen Übertragungs- und Verarbeitungsgeschwindigkeiten, immer weniger ins Gewicht fällt, ob man nur 100 oder gleich 1000 Bytes für einen Kunden laden lädt, kann man das natürlich ohne weiters realisieren, ohne von OOP abweichen zu müssen. Im Gegenteil, gerade die Kapslung von OOP ermöglicht eine einfache Realisierung.

Du schreibst eine vollständige Klasse Kunden. Du erzeugst das Kunden-Objekt aber erstmal nur mit den grundlegenden Informationen. Die Properties, die andere Informationen erhalten, werden so realisiert, dass die restliche Daten per Bedarf nachladen werden (das ist natürlich etwas vereinfacht ausgedrückt, denn das Laden selber macht natürlich immer der DAL, aber den Anstoß dazu gibt die Property).

Nun kann es Situationen geben, in denen dieses Verhalten das Gegenteil des gewünschten Effekts bewirkt, nämlich dann, wenn du in der Liste der Kunden auch noch der Umsatz des Kunden angezeigt werden soll, obwohl nur Kundennummer, Name und Vorname geladen wurden und somit für jeden der vielen einzelnen Kunden eine einzelne Datenbankanfrage gestellt werden muss. Um das zu vermeiden, sollte es (z.B. per Konstruktor-Parameter) möglich sein, zu bestimmen, ob ein Objekt gleich ganz oder erstmal nur teilweise geladen wird.

herbivore

2.187 Beiträge seit 2005
vor 13 Jahren

Hallo larsux,

Meine Empfehlung wäre es gleich das ganze Kunden-Objekt zu laden (es hat ja nicht 200 Properties/Spalten in der DB, oder?).
Warum? Weil man irgend wann eh andere Daten Anzeigen muss und dann kann man einfach nur die UI (aspx) ändern.

Gruß
Juy Juka

L
larsux Themenstarter:in
8 Beiträge seit 2010
vor 13 Jahren

Danke erstma für eure Antworten. Also ich werde es wahrscheinlich so machen dass ich wirklich gleich alles ins KundenObjekt lade. Es sind 22 Eigenschaften die ich jedem Objekt mitgebe....

Was mir grad noch so einfällt. Ich lade ja beim Start der Webanwendung dann alle Kunden in Objekte und speichere diese dann in der Session. Die Sessiontime beträgt per default 20 min.

Wenn nun in der Zwischenzeit ein Kunde hinzu kommt ist der für den Nutzer mit der momentanen Session nicht sichtbar außer ich lade bei jedem PostBack bzw. Aufruf der Startseite alle Kunden neu. Aber das ist doch auch wieder ein wenig ineffektiv. Oder ich könnte ja auch einfach ein Count auf die DB schicken und schauen ob die Anzahl der Kunden in der DB mit meiner Objekt Anzahl übereinstimmt und wenn das nicht der Fall ist lade ich alle Kunden neu!? Dabei hab ich aber wieder nur die Anzahl und kann nicht schauen ob sich Daten der einzelnen Kunden geändert haben. Ach man das ist alles so verzwickt... O_o

Wie geht man am Besten vor???

Danke!

Gruß larsux....

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo larsux,

Dabei hab ich aber wieder nur die Anzahl und kann nicht schauen ob sich Daten der einzelnen Kunden geändert haben.

wenn die Anzahl nicht stimmt (oder auch von Anfang an), kannst du die Kundennummern laden, um einen Vergleich zu ermöglichen, welche Kunden hinzugekommen sind.

herbivore

L
larsux Themenstarter:in
8 Beiträge seit 2010
vor 13 Jahren

Ja das ist klar aber in den Moment prüfe ich halt nur ob Kunden hinzugekommen bzw. gelöscht wurden aber ich prüfe ja dann nicht ab ob sich Daten eines einzelnen Kunden gändert haben.

Was vllt. auch noch möglich wäre ist das man zu jedem Kunden ein Attribut "zuletzt_bearbeitet" in der Datenbank setze. Und dann hol ich mir nachdem das Count in der Datenbank die gleiche Anzahl wie die Anzahl der Objekte gelifert hat, die Kundennummer und zuletzt_bearbeitet und prüfe das ab. Und wenn ich feststelle dass sich bei einem Kunden etwas geändert hat lade ich nur die Informationen zu diesem Kunden neu... 😉

Gruß larsux....

2.187 Beiträge seit 2005
vor 13 Jahren

Hallo larsux,

Um wie viele Kunden-Daten-Sätze geht es den?
Musst du eigentlich immer alle Datensätze laden, oder kann nicht der Benutzer entscheiden wann er neue Datensätze anfordert?
Am besten Fordert der Benutzer Datensätze mit Kriterien an, so dass du nicht alle Datensätze auf einmal laden musst.

Gruß
Juy Juka

S
443 Beiträge seit 2008
vor 13 Jahren

Ich habe mich für eine ChangeDate-Spalte entschieden
SELECT * FROM Adressen WHERE ChangeDate > #HöchstesLokalesChangeDate#
und alles was da rauskommt wurde angelegt oder bearbeitet

Hint am Rande:
Ich lösche keine Datensätze sondern
UPDATE Adressen SET Deleted = true, ChangeDate = now WHERE KeyId = bla
sprich ich markiere sie als gelöscht, somit werden gelöschte Adressen zum client übertragen welcher diese dann lokal auch als gelöscht markieren kann/wegschmeissen kann.

mbg
Rossegger Robert
mehr fragen mehr wissen

Montag morgen ist die beste Zeit um eine erfolgreiche Woche zu beginnen

L
larsux Themenstarter:in
8 Beiträge seit 2010
vor 13 Jahren

Um wie viele Kunden-Daten-Sätze geht es den?
Musst du eigentlich immer alle Datensätze laden, oder kann nicht der Benutzer entscheiden wann er neue Datensätze anfordert?

Na momentan sind es ca. 200. Ich könnte den Nutzer auch gleich suchen lassen aber ich wollte ihm halt auch noch die Möglichkeit geben zu scrollen und einen Kunden direkt auszuwählen aber hab mir grad überlegt dass das vllt nicht gerade das Sinnvollste ist^^ 😄

@spike24:
Auch ein guter Ansatz...

Danke für eure Hilfe!!!!

Gruß larsux....

L
larsux Themenstarter:in
8 Beiträge seit 2010
vor 13 Jahren

Hab da mal noch eine Frage:

Nehmen wir mal an mein Kundenobjekt hat feste Eigenschaften aber auch noch Eigenschaften die in der Anzahl variieren können. Je nachdem wieviel Informationen(Spalten) in der Datenbank hinzukommen bzw. wegfallen.

Ich würde dann eine Extra Klasse bspw. C_KundenDatenVariabel (Eigenschaften: KundenAttrName, KundenAttrWert) erstellen und irgendwie eine ganze Liste(List<C_KundenDatenVariabel>) mit den ganzen Werte einer Eigenschaft des Kundenobjekts hinzufügen.

Aber wie müsste dann die set Methode aussehen oder ist der Ansatz generell nicht opimal?

public class C_Kunde {
private int _KndNr;
private string Name;
private string Vorname;
private List<KundenDatenVariabel> _KundenDatenVariabel;
.....
public string KndNr {
   get { return _KndNr; }
   set { _KndNr = value; }
}

public List<C_KundenDatenVariabel> KundenDatenVariabel;

public List<C_KundenDatenVariabel> KundenDatenVariabel {

        get { return _KundenDatenVariabel; }
        set { 
            ?????????????????????; 
        }
    }
......
public List<C_Kunde> getKunden() {
  List<C_Kunde> Kundenliste = new List<C_Kunde>;
  sqlQuery = "SELECT * FROM Kunden;";
  .....
  while (sqlReader.Read) {
     C_Kunde Kunde = new C_Kunde();
     Kunde.KndNr = sqlReader.GetValue(0);
     .......
     Kundenliste.Add(Kunde);
  }
  return Kundenliste;
}
}

public class C_KundenDatenVariabel()
{
   private string _KundenAttr;
   private string _KundenAttrWert;

  public string KundenAttr {
    get { return _KundenAttr }
    set { _KundenAttr = value }
}

public string KundenAttrWert {
    get { return _KundenAttrWert }
    set { _KundenAttrWert = value }
}

}

Gruß larsux....

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo larsux,

einen Setter brauchst du überhaupt nicht. Aber dein Ansatz ist eh nicht gut. Wenn du sowas machen willst, verwende intern ein Dictionary. Die Klasse sollte weiterhin Kunde heißen und das Dictionary innerhalb der Klasse verwendet werden. Das sind alles Implementierungsdetails, die nicht an außen bekannt sein sollten.

herbivore

L
larsux Themenstarter:in
8 Beiträge seit 2010
vor 13 Jahren

Ok. Da muss ich mich mal einlesen. Vielen Dank erstmal für die Hilfe!

Bin seit heut angemeldet und das Forum gefällt mir echt gut.

Danke und schönen sonnigen Tag noch! 😁

Gruß larsux....

T
179 Beiträge seit 2007
vor 13 Jahren

Wäre es nicht am einfachsten, wenn man meinen Getter und eine interne Variable nimmt und beim ersten Aufruf des Getters dann die Daten lädt, in die interne Variable speichert und die dann bei den nächsten Aufrufen abfragt?

B
40 Beiträge seit 2008
vor 13 Jahren

Hallo,

warum machst du das nicht mit dem SQL Cachedependency?
http://www.asp.net/data-access/tutorials/using-sql-cache-dependencies-vb

Das Liefert dir auch nur die geänderten Daten.

Generell alle Datensätze in die Session legen, halt ich für Unklug, weil du immer die komplette Session rüber zum Server und wieder zurück senden musst.

Das Kann bei einem größerem Projekt echt Probleme mit den Zugriffszeiten geben.

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo t-master,

Wäre es nicht am einfachsten, wenn man meinen Getter und eine interne Variable nimmt und beim ersten Aufruf des Getters dann die Daten lädt, in die interne Variable speichert und die dann bei den nächsten Aufrufen abfragt?

quasi das gleiche hatte ich ja oben vorgeschlagen. Allerdings mit dem wichtigen Unterschied, dass bei beim ersten Zugriff auf nicht vorhandene Daten, das ganze Objekt geladen wird und nicht nur die gerade angeforderte Property. Denn sonst braucht man ja für jedes Objekt soviele einzelne Datenbankzugriffe wie nicht vorhandene Properties abgefragt werden. Das wäre total ineffizient.

herbivore

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo zusammen,

in .net 4.0 gibts für Delayed Loading mit der Lazy<T>-Klasse eine Möglichkeit.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

L
larsux Themenstarter:in
8 Beiträge seit 2010
vor 13 Jahren

Wenn du sowas machen willst, verwende intern ein Dictionary. Die Klasse sollte weiterhin Kunde heißen und das Dictionary innerhalb der Klasse verwendet werden.

Also ich hab das jetzt mal mit dem Dictionary probiert aber an einer Stelle hängt es noch. Ich gebe das Dictionary der Klasse Kunden als Property. Und dann übergebe ich das Dictionary mit den Daten an die Property der Klasse Kunde allerdinmgs kommt es dann immer zu einem Fehler... 😦


public class C_Vorgang
{
  private Dictionary<string, string> _MetaData;
 
  public Dictionary<string, string> MetaData {
        get { return MetaData; }
        set { _MetaData = value; }
    }
}

im eigentlichen Code:

Dictionary<string, string> AllgemeineInf = new Dictionary<string, string>();
                    foreach (XmlNode xmlN1 in AllgemeineData)
                    {
                        if (xmlN1.SelectSingleNode("Wert") != null)
                        {
                            AllgemeineInf.Add(xmlN1.SelectSingleNode("Beschreibung").InnerXml, xmlN1.SelectSingleNode("Wert").InnerXml);
                        }
                        else {
                            AllgemeineInf.Add(xmlN1.SelectSingleNode("Beschreibung").InnerXml, "");    
                        }
                        
                    }
                    Vorgang.MetaData = AllgemeineInf; //Hier brichght der Debugging Prozess einfach ab :(
                    Vorgangsliste.Add(Vorgang);

Was hab ich falsch verstanden/gemacht?

Gruß larsux....

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo larsux,

allerdinmgs kommt es dann immer zu einem Fehler

Siehe [Hinweis] Wie poste ich richtig? Punkt 5. Bei sowas immer alle Exceptions fangen.

herbivore

L
larsux Themenstarter:in
8 Beiträge seit 2010
vor 13 Jahren

Mh, irgendwie wird keine Exception geworfen. Der integrierte Webserver von Visual Studio bricht einfach zusammen^^ 😦


                  try
                    {
                        Vorgang.MetaData = AllgemeineInf;
                    }
                    catch (Exception ex) {
                        Response.Write(ex.Message);
                    }

Ich hab grad gesehn dass in der Dictionary Property(MetaData) des Vorgangsobjekts steht "Die Funktionauswertung wurde abgebrochen!

Jemand ne Idee?

Gruß larsux....

61 Beiträge seit 2009
vor 13 Jahren

Zur Eingangsfrage:

Meine Idee wäre die Datensätze seitenweise auszugeben: z.B. nur 10 auf jeder Seite.
Mit "SELECT TOP 10 * FROM table;" kann man die ersten 10 ausgeben, aber ich bin in SQL nicht so bewandert, doch ich denke dass es möglich ist.

Vorteil:
Es werden beim Anzeigen immer nur maximal 10 Datensätze gezeigt (oder ggf. auch gerne mehr), d.h. es werden auch nur immer 10 Datensätze aus der DB geholt. Mit "SELECT COUNT(*) FROM table;" könnte man die gesammtzahl ermitteln und man müsste nur noch 1-10/200 oder 11-20/200 angeben, aber leider weiß ich nicht wie man auf die Position der Datensätze so kommen könnte.

In der Zeit vor fünf Minuten ist Jetzt die Zukunft. Jetzt ist die Gegenwart. Die Zeit, in der ich zu erzählen begonnen habe, ist die Vergangenheit von Jetzt und die Zukunft von der Gegenwart der Zeit, fünf Minuten bevor ich zu erzählen begann.