Laden...

Objektorientierte Datenzugriffsschicht

Erstellt von JuyJuka vor 16 Jahren Letzter Beitrag vor 16 Jahren 17.660 Views
JuyJuka Themenstarter:in
2.187 Beiträge seit 2005
vor 16 Jahren
Objektorientierte Datenzugriffsschicht

Ich bin nicht sicher, ob wir nicht zu weit vom eigentlichen Thema abschweifen. Ich werd eine Beispielklasse posten, wenn Ihr wollt. Dauert aber noch etwas.

JuyJuka Themenstarter:in
2.187 Beiträge seit 2005
vor 16 Jahren

So, hier kommt eine kleine Beispiel-Klasse.
Im Moment muss man noch den genauen Typ kennen aber an dem Problem arbeite ich gerade.
Vielleicht sollten wir das hier in einen anderen Thread verschieben?


namespace Beispiel
{
  /// <summary>
  /// Diese Klasse stellt einen Transport von Ort Ziel zum Ort Quelle dar.
  /// </summary>
  [ // Information, in welcher Tabelle die Datensätze abgelegt werden.
  Aisys.ReflectionVersion.Tabelle(/*Tabellen-Name*/"transport")]
  [ // Bestimmt, ob automatisch ein Protokoll über alle Felder geführt werden soll.
  Aisys.ReflectionVersion.Protokolliert(/*ja/nein==true/false*/true)]
  [ // Da keine Logik in der Datenbank sein soll und da nicht in jedem DBMS Autowerte möglich sind, 
    // werden Autowerte von der Anwedung vergeben. 
    // (SQL-Tricks mit Transaktionen und UPDATE-WHERE-Klauseln von unserem SQL-Guru)
  Aisys.ReflectionVersion.AutoWert(/*Spaltenname*/"id",/*Kennzeichen des Autowerts*/"transport")]
  public class Transport : Aisys.AisysObject
  {
    #region Normale Attribute für die Daten, mit standard Werten
    private decimal transportID = decimal.MinValue;
    private Ort ziel = null;
    private decimal zielID = decimal.MinValue;
    private Ort quelle = null;
    private decimal quelleID = decimal.MinValue;
    private string zusatz = string.Empty;
    private TransportStatus status = TransportStatus.Offen;
    #endregion

    [ // Information, in welcher Spalte der Wert abgelegt wird.
    Aisys.ReflectionVersion.Data(/*Spaltenname*/"id")]
    [ // Makierung für Spalten, die zum Primärschlüssel gehören.
    Aisys.ReflectionVersion.ID()]
    public decimal TransportID
    {
      get { return this.transportID; }
      private set { this.transportID = value; }
    }

    [ // Information, in welcher Spalte der Wert abgelegt wird.
    Aisys.ReflectionVersion.Data(/*Spaltenname*/"zusatz")]
    public string Zusatz
    {
      get { return this.zusatz??string.Empty; }
      set { this.zusatz =value??string.Empty; }
    }

    [ // Information, in welcher Spalte der Wert abgelegt wird.
      // Enums werden automatisch umgewandelt
    Aisys.ReflectionVersion.Data(/*Spaltenname*/"status")]
    public TransportStatus Status
    {
      get { return this.status; }
      private set { this.status = value; }
    }

    [ // Die meisten Typen werden nicht automatisch unterstützt, 
      // deshalb wird auf ein anders Property zum Mappen verwiesen
    Aisys.ReflectionVersion.Refer(/*Property als String, da es in C# nicht anders möglich ist*/"DBZiel")]
    public Ort Ziel
    {
      get
      {
        if (this.zielID != decimal.MinValue && this.ziel == null)
        // Prüft ob ein Ziel zugewisen ist (!=decimal.MinValue) und 
        // ob das ziel noch nicht geladen ist (==null)
        {
          // Es wird eine Selectabfrage für Ort-Objekte erzeugt.
          Aisys.ExtendedSelect.Select<Ort> select = Aisys.ExtendedSelect.SelectFactory.Create<Ort>();
          select.Objekt = new Ort(this.zielID);
          select.Bedingung = new Aisys.ExtendedSelect.PropertyBedingung<Ort>(
                             /*Property als String, da es in C# nicht anders möglich ist*/
                             "OrtID");
          // select.ToSQL() => SELECT * FROM [Tabelle für Ort-Objekte] WHERE 1=1 AND [Spalte von OrtID] = [Wert aus select.Objekt.OrtID] 
          this.ziel = select.ExecuteAsForeigenKey();
        }
        return this.ziel;
      }
      set
      {
        this.ziel = value;
        this.zielID = (value == null ? decimal.MinValue : value.OrtID);
      }
    }

    [ // Information, in welcher Spalte der Wert abgelegt wird.
      // Privates Property, nur zum Mappen
    Aisys.ReflectionVersion.Data(/*Spaltenname*/"ziel")]
    private decimal DBZiel
    {
      get { return this.zielID; }
      set { this.zielID = value; }
    }

    [ // Die meisten Typen werden nicht automatisch unterstützt, 
      // deshalb wird auf ein anders Property zum Mappen verwiesen
    Aisys.ReflectionVersion.Refer(/*Property als String, da es in C# nicht anders möglich ist*/"DBQuelle")]
    public Ort Quelle
    {
      get
      {
        if (this.quelleID != decimal.MinValue && this.quelle == null)
        // Prüft ob ein Ziel zugewisen ist (!=decimal.MinValue) und 
        // ob das ziel noch nicht geladen ist (==null)
        {
          // Es wird eine Selectabfrage für Ort-Objekte erzeugt.
          Aisys.ExtendedSelect.Select<Ort> select = Aisys.ExtendedSelect.SelectFactory.Create<Ort>();
          select.Objekt = new Ort(this.quelleID);
          select.Bedingung = new Aisys.ExtendedSelect.PropertyBedingung<Ort>(
            /*Property als String, da es in C# nicht anders möglich ist*/
                             "OrtID");
          // select.ToSQL() => SELECT * FROM [Tabelle für Ort-Objekte] WHERE 1=1 AND [Spalte von OrtID] = [Wert aus select.Objekt.OrtID] 
          this.quelle = select.ExecuteAsForeigenKey();
        }
        return this.quelle;
      }
      set
      {
        this.quelle = value;
        this.quelleID = (value == null ? decimal.MinValue : value.OrtID);
      }
    }

    [ // Information, in welcher Spalte der Wert abgelegt wird.
      // Privates Property, nur zum Mappen
    Aisys.ReflectionVersion.Data(/*Spaltenname*/"quelle")]
    private decimal DBQuelle
    {
      get { return this.quelleID; }
      set { this.quelleID = value; }
    }

    // Eine einfache Funktion
    public void Beginnen()
    {
      this.Status = TransportStatus.InArbeit;
      this.Save();
    }

    // Eine einfache Funktion
    public void Beenden()
    {
      this.Status = TransportStatus.Erledigt;
      this.Save();
    }

    // Eine einfache Funktion
    public void Stornieren()
    {
      Transport retoure = null;
      if (this.Status == TransportStatus.Erledigt
      || this.Status == TransportStatus.InArbeit)
      {
        retoure = new Transport();
        retoure.Ziel = this.Quelle;
        retoure.Quelle = this.Ziel;
      }
      // Mehrer Objekte in einer Transanktion speichern.
      Aisys.AisysObject.SaveAsTransaction(this, retoure);
    }
  }

  public enum TransportStatus
  {
    Offen,
    InArbeit,
    Erledigt,
    Storniert
  }

  #region Ort
  public class Ort : Aisys.AisysObject
  {
    public Ort() { }
    public Ort(decimal id) { this.OrtID = id; }
    public decimal OrtID 
    { 
      get { return decimal.MinValue; } 
      private set { } 
    }
  }
  #endregion
}

3.728 Beiträge seit 2005
vor 16 Jahren
Geschäftslogik oder Datenzugriff

Diese Klassen kapseln den Datenzugriff objektorientiert. Du nennst sie aber "Business-Klassen" (vgl. typed oder untyped Dataset). Ich würde bei Business-Klassen vermuten, dass sie die Kern-Geschäftslogik enthalten. Wie sieht es mit Berechnungen etc. aus? Packst Du die auch direkt in diese Klassen?

JuyJuka Themenstarter:in
2.187 Beiträge seit 2005
vor 16 Jahren

Wenn keine weiterführenden Anforderungen vorliegen, kommen Berechnungen und sowas auch in diese Klassen.
Meist wird aber noch Flexibilität oder Ähnliches verlangt, dann wende ich noch Patterns/Entwurfsmuster an (z.B. Statepattern, Templatepattern, ...). Dann wird die Geschäftslogik wieder ausgelagert.
Trotzdem Bilden immer diese Klassen die "Schnittstellen" für die UI (Ich weiß selber, dass hier Interfaces angesagt wären, aber an dem Problem arbeite ich gerade nocht.).

Ungefähr so:


public class Transport
{
  private ITransporArt art;

  public void Ausfuehren()
  {
    this.art.Ausfuehren(this);
  }
}

Gruß
Juy Juka

3.728 Beiträge seit 2005
vor 16 Jahren
Alles in einem

Deine Klassen sind also alles in einem. Sie enthalten:*Definition der Datenstrukturen *Datenzugriffslogik *Geschäftslogik

Das erinnert mich spontan an die Objektmodelle von Word und Outlook. Ein durchgängiges Objektmodell. Nur sind Word und Outlook reine Desktop-Anwendungen (also 1-Tier). Auch Adobe Photoshop und Adobe InDesign haben Objektmodelle in der Art. Aber auch das sind reine Desktop-Anwendungen.
Kein Datenbank-Server, kein Applikations-Server. Einfach nur ein monolithischer Prozess.
Für reine Desktop-Anwendungen mag dieses Konzept super sein, aber nicht für mehrschichtige, verteilte Geschäftsanwendungen. Wenn alles in einer Klasse steht, kann man keine Schichten bilden und diese in verschiedenen Prozessen bzw. auf verschiedenen Computern laufen lassen. Selbst wenn Du sagst: "Eine Mittelschicht, die alles abdeckt, genügt mir!", hast Du ein Problem. Deine Business-Klassen skalieren nicht gut. Sie eignen sich deshalb nicht für den Betrieb auf einem Anwendungsserver. Grund dafür ist, dass sie statusbehaftet sind. Wenn ein Client z.B. zwei Angebote mit jeweils 10 Positionen bearbeitet, müssten auf dem Anwendungsserver 22 Objekte über die komplette Bearbeitungsdauer im Hauptspeicher gehalten werden. Wenn es 10 gleichzeitige Benutzer sind, die Angebote erfassen, wären das schon 220 Objekte. Die Server-Ressourcen würden damit geradezu verschwenderisch verpulvert und wären bereits bei wenigen gleichzeitigen Anwendern aufgebraucht. 2 Angebote mit 10 Positionen sind nur ein kleines Beispiel. Überlege mal, wie viele Objekte ein Sachbearbeiter wirklich den Tag über öffnet und bearbeitet. Die meiste Zeit sitzt er vor dem Monitor und schaut sich die Präsentation der Daten an. Nur innerhalb ein paar kurzer Klicks, werden wirklich Daten abgerufen und gespeichert. Trotzdem würden Deine statusbehafteten Business-Objekte die Ganze Zeit Server-Ressourcen verbrauchen. Stell Dir vor, es arbeiten 300 Benutzer gleichzeitig mit Deiner Anwendung. Wie groß müsste die Hardware des Applikations-Servers dimensioniert sein, damit alle flüssig arbeiten können, wenn es sich um eine 3-Tier Anwendung handelt?

Es ist alles andere als sinnvoll, Server-Ressourcen zu verschwenden. Man sollte Server-Ressourcen so kurz wie möglich (also z.B. nur genau in den 40ms, in denen wirklich Daten abgerufen oder gespeichert werden) reservieren und so schnell wie möglich wieder freigeben. Das ist aber nur mit statuslosen Geschäftsklassen und passiven DTOs (bzw. DataSets/DataTables) möglich.

Deshalb bin ich der Meinung, dass solche aktiven Objektmodelle, wie Du sie mit Deinen Business-Klassen aufbaust, gut für Desktop-Anwendungen wie Word oder Photoshop sind, aber für n-Schichtige und besonders verteilte Anwendungen völlig ungeeignet und sogar schädlich. Fat-Client und DB-Server ist das höchste er Gefühle, was diese Architektur verträgt. Wenn Du damit auf Dauer auskommst, sind die Objekte okay, wenn aber nicht, solltest Du Deine Architektur nochmal überdenken.

JuyJuka Themenstarter:in
2.187 Beiträge seit 2005
vor 16 Jahren

Hallo,

Stimmt wohl, Geschäftslogikt und Definition (Datenstruktur) liegen beide in meinen Klassen. Die Datenzugriffslogik liegt jedoch in meinem Framework dahinter.

Das meine Objekte auf Servern viele Resourcen verbrauchen ist ein Aspekt, der mir bis jetzt nicht klar war. Ich kann meine Objekte ja schlecht vom Server weg schicken, die müssen dort im Arbeitsspeicher verbleiben.

Mhm...
Wie könnte man eine resourcenschonde Architektur aufbauen und diese vor der Präsentationsschicht verbergen? Also so, dass auch der UI Seite immer noch nur ein normales Interface bekannt ist und man einfach sagt instanzVonIIrgendwas.Mach();

Ich fände es schon wichtig und für die Entwicklung in unserer Firma wär es auch wichtig, dass der Wechsel zwischen n-Tier- und FatClient-Architektur keine Änderungen in der Prästenstatinsschicht nach sich zieht (außer in der App.config halt). Man kann (laut unserem Vertrieb) eine viel größere Kundengruppe ansprechen, wenn man mehrere Architekturen anbieten kann.

Gruß
Juy Juka

F
10.010 Beiträge seit 2004
vor 16 Jahren

Schon mal daran gedacht, das ein nicht unbeträchtlicher Teil der SW-Welt
i.M. eher auf Smartclient statt auf fette Appserver setzt?

Der Server als solcher ist da nur zur Datenhaltung gedacht, die vorhandene
Rechenleistung der Clients kann mit einfachen sachen wie OfflineDB's und co
genauso einfach skalieren, ohne das man hunderte Barrieren durchbrechen muss.

Wenn Du lieber mit einem Appserver arbeitest, deine Sache, der vernünftig gemachte SmartClient
kann die Aufgabe genauso erledigen.

Ist wie bei vielen Sachen in der SW-Entwicklung eine reine Frage der Anschauung.

JuyJuka Themenstarter:in
2.187 Beiträge seit 2005
vor 16 Jahren

Danke FZelle, SmartClient-Architektur ist eine super Idee, die ich bei uns in der Firma auch mal Fallen lassen werde.
Es ist eigentlich nur eine Erweiterung (ein Deployment-System) die man auf alle "Client-Server"-Architekturen anwenden kann, oder?

T
108 Beiträge seit 2005
vor 16 Jahren

Hallo Zusammen,

ich finde Eure Diskussion sehr interessant. Die Anmerkungen von Rainbird sind für mich absolut nachvollziehbar. Skalierbarkeit ist ein sehr wichtiges Thema, welches eine sehr wichtige Rolle bei Geschäftsanwendungen einnimmt.

Ich selbst bin nur ein privat-Entwickler und beschäftige mich einfach aus Interesse mit der Umsetzung. Beruflich bin ich für das Management und Monitoring der Datenbank- und Applikationsserver zuständig. Anwendungen, die nicht oder nur schlecht skalieren, machen mir dabei häufig große Probleme, da die Last sehr schnell steigt.

Da hier im Forum die Diskussion bzw. Fragen zur Layer und Tier Entwicklung immer häufiger kommen, wäre es schön, wenn ein entsprechendes Beispiel mal vorliegen würde. Ja ich weiss, Rainbird hat das MVC-Beispiel gepostet, aber ich erkenne hier (vielleicht weil mir die Erfahrung / Kenntniss fehlt) die Zerlegung in Tiers nicht.
Die AdventureWorks Cinema Anwendung nutzt leider auch kein ADO.Net sondern typisierte Listen.

Es wäre also schön, mal ein Beispiel für eine echte n-Tier Anwendung zu haben. Ich selbst bin nicht in der Lage ein entsprechendes Beispiel zu erstellen, da mir die Erfahrung fehlt.

Gruß
Tokka

Was einmal war, wird nie wieder sein...

1.378 Beiträge seit 2006
vor 16 Jahren

Da schließ ich mich doch gleich mal an 🙂

Wenn einer der Tier-Profis ein Beispiel erstellen könnte, wäre das bestimmt für viele sehr hilf/lehrreich.

Lg XXX

3.728 Beiträge seit 2005
vor 16 Jahren
Beispiel

Ich werde gerne ein Beispiel posten, aber das kann etwas dauern, da solche Beispiele schon etwas aufwändiger sind. Shließlich braucht eine 3-Tier-Anwendung eine gewisse Infrastruktur. Vor allem einen Applikationsserver.

T
108 Beiträge seit 2005
vor 16 Jahren

Original von Rainbird
Ich werde gerne ein Beispiel posten, aber das kann etwas dauern, da solche Beispiele schon etwas aufwändiger sind. Shließlich braucht eine 3-Tier-Anwendung eine gewisse Infrastruktur. Vor allem einen Applikationsserver.

Gute Morgen Rainbird,

"Gut Ding will Weile haben!" dehalb sei Dir die Zeit gewährt, um uns Unwissende in die Untiefen der n-Tier architektur einzuführen 😁

Freue mich schon auf Dein Beispiel.

Gruß
Tokka

Was einmal war, wird nie wieder sein...

F
10.010 Beiträge seit 2004
vor 16 Jahren

@Tokka:
Natürlich benutzt Adventureworks Cinema ADO.NET, anders geht in .NET der DB zugriff nicht.

@Rainbird:
Uuuuuh, Glaubenskriege.
Ich habe hier schon mehrfach geschrieben, das eine Enterpriseapp mit Appserver
deutlich besser und kostengünstiger skaliert als eine reine DB-Server basierte Anwendung.

Nur ab wann braucht man das?

Die wenigsten benötigen soetwas wirklich, und diese anderen fahren mit einem
durchdachten Smartclient deutlich besser.

3.728 Beiträge seit 2005
vor 16 Jahren
Größer werden

@FZelle: Natürlich sollte man nicht mit Kanonen auf Spatzen schießen. Aber Projekte werden auch größer und es kommen ständig neue Anforderungen hinzu. Verstärkt im Bereich Automatisierung und Kommunikation mit anderen Softwaresystemen. Auch auf Sicherheit wird mehr Wert gelegt als früher. Das sind alles Dinge, die eine klassische Fat-Client-Anwendung nicht so gut unterstützt. Besonders der Punkt "Anbindung an andere Softwaresysteme" sieht bei den meisten Fat-Clients ganz düster aus.

Und da der Aufwand eine n-Tier-Anwendung zu erstellen, mit .NET wirklich nicht mehr groß ist (im Vergleich zu früher), sollte man auch bei kleinen Projekten darüber nachdenken. Verteilte Anwendunegn sind nicht der Heilige Gral aber bieten viele Vorteile.

1.274 Beiträge seit 2005
vor 16 Jahren

Hallo zusammen,

Smartclient's sind in der regel meist auch n-tier Anwendungen. Der Zugriff erfolgt meist über eine Webservice auf die Datenbank.

Verteilung der Tiers

  1. SmartClient App -> Präsentations-Schicht
  2. Webservice -> Übertragungs-Schicht
  3. Business Logic -> Business Schicht
  4. Data Access -> Datenzugriff Schicht
  5. Database -> Datenbank

Wenn man nun es geschickt architektural anwendet und das System so aufbaut das man die Übertragungsschicht weglassen kann, so ist es auch für kleine Single User Anwendungen genauso geeignet wie für Enterprise Apps wo es über 3 gereicht wird.

Lg.
LastGentleman

"Das Problem kennen ist wichtiger, als die Lösung zu finden, denn die genaue Darstellung des Problems führt automatisch zur richtigen Lösung." Albert Einstein

F
10.010 Beiträge seit 2004
vor 16 Jahren

Richtig, wenn man SmartClients richtig aufbaut ( schon mit CAB und den Services gearbeitet? )
dann ist der Umstieg, wenn es dann nötig wird ziemlich simple.

Aber man benötigt nicht gleich am anfang den Appserver, kann ihn aber bei zeiten einfügen.
Das machen moderne System dann auch möglich 😉

Wobei die Aufteilung dann eher so aussieht.

  1. SmartClient App - > Gui auf MVP basis.
  2. BusinessLogic -> Ist aufgeteilt in den Lokal Part in dem WorkItem ( UseCase kapselung)
    und den Services.
  3. Datenzugriff -> Services erledigen dies.

Bei CAB/SCSF lässt sich in einem WorkItem/UseCase nachträglich
jeder Service simplst austauschen, ohne neukompilierung, so kann man
mit einer Textdatenhaltung anfangen, dann auf eine Locale DB umschalten,
und ggf auch eine Remote oder Appserver Anbindung realisiern.

Auch die Teile der BL die als service implementiert sind sind einfachst zu tauschen.

So braucht man keine Spatzen wenn man kanonen, äh ne anders rum.
Aber man kann auch Perschings besorgen und benutzen.

1.274 Beiträge seit 2005
vor 16 Jahren

Hallo FZelle,

danke für deinen Kommentar, ich hab leider noch nicht viel mit CAB gemacht, ich schaue mir gerade sehr viel im Bereich Software Architektur an damit ich eine Solide Grundlage habe vorauf ich bauen kann.

Diesen Ansatz findet man ja schon in SOA wo alles nur Services und die Logik die dahinter liegt ausgetauscht werden kann.

Das schöne bei dem Ansatz mit CAB ist ja auch das es relativ Leicht ist die Zwischenschicht wie eine Datenbank oder eine Webservice einzufügen.

"Das Problem kennen ist wichtiger, als die Lösung zu finden, denn die genaue Darstellung des Problems führt automatisch zur richtigen Lösung." Albert Einstein

F
10.010 Beiträge seit 2004
vor 16 Jahren

Wobei SOA und Services in CAB nichts mit einander zu tun haben.

Schau dir nicht CAB alleine an, sondern SCSF

M
110 Beiträge seit 2007
vor 16 Jahren

Hallo,

um eine solide Basis zu haben kann ich ein Buch empfehlen "Framework Design Guidelines". Da stehe viele Information zu einem vernünftigen Design drin. Vor allem sind viele Dinge sehr logisch, die aber in den meisten Unternehmen nicht angewendet werden. Da ich grundsätzlich beim Kunden vor Ort in den Projekten entwickel, denke ich, dass sich weiss wovon ich rede[🙂]

Man findet sich sehr schnell zurecht, wenn man bestimmte Regeln beachtet. Falls ein Unternehmen allerdings meint, gegen den Strom schwimmen zu müssen, braucht man sich nicht zu wundern, wenn neue Mitarbeiter lange Einarbeitungszeiten haben. Das ist meistens keine Sache der komplexität, sondern eher etwas der Architektur.

Gruss

Mirko

Mappen statt hacken mit Invist , dem .NET O/R Mapper - Code Generator

F
10.010 Beiträge seit 2004
vor 16 Jahren

Wobei in vielen Unternehmen der begriff Architektur unbekannt ist.

3.728 Beiträge seit 2005
vor 16 Jahren

Ich werde gerne ein Beispiel posten, aber das kann etwas dauern, da solche Beispiele schon etwas aufwändiger sind ...

Das Beispiel-Projekt kommt gut voran (AppServer steht!).
Hier gibts die ersten Ergebnisse: .NET Applikationsserver

Edit:

Das Beispiel-Projekt ist jetzt lauffähig und kann unter obigem Link heruntergeladen werden.
Da dieser Thread hier der eigentliche Auslößer war, das Beispiel-Projekt zu machen und ich schon einige Abende dafür investiert habe, erwarte ich nun auch ein ausführliches Feedback.