Ergebnis 1 bis 14 von 14

Persistence Service bei wechselnden Datenobjekten

  1. #1 Zitieren
    Ritter Avatar von Delta 38
    Registriert seit
    Nov 2008
    Ort
    Bremen
    Beiträge
    1.251
    Hallo liebes Forum

    Ich sitze seit ein paar Wochen an einem für mich unlösbaren Problem. Folgende Situation: Ich habe ein Datenobjekt 'Konto', welches irgendwie auf der Festplatte/Datenbank/etc. gespeichert werden muss. An sich nichts leichter als das. Hinzu kommt jedoch die Problematik, dass die Benutzeroberfläche dem Konto noch andere Daten, wie beispielsweise ein Icon zuordnet. Diese zusätzlichen Daten machen aber für eine Konsolenanwendung keinen Sinn, sollten also nicht im Datenobjekt selbst gespeichert werden.

    Wie baue ich nun eine Persistence-Klasse, die mir die Möglichkeit bietet "generische" Datenobjekte (Konto, KontoFormsViewItem, KontoConsoleViewItem, etc.) abzuspeichern? Bisher muss ich für jede neue Benutzeroberfläche eine neue Persistence Klasse schreiben. Im Anhang mal ein kleines UML-Diagramm.

    Viele Grüße,
    Delta

    [Bild: uml.png]
    Delta 38 ist offline

  2. #2 Zitieren
    Pretty Pink Pony Princess  Avatar von Multithread
    Registriert seit
    Jun 2010
    Ort
    Crystal Empire
    Beiträge
    11.228
    Geht es um ein simples Abspeichern von Daten, oder ist da noch etwas Komplexeres dahinter?

    Ich sehe das Problem nicht:
    3-4 Verschiedene Klassen, welche jeweils als Ganzes abgespeichert werden
    1. Konto
    2. Icon
    3. ..

    Im Objekt selber ist bei den Properties, welche Gespeichert/Geladen werden müssen ein Attribut angefügt.
    Und im Obersten Objekt gibt es eine Verbindungsinformation zum Persistenten Speicher.

    Das Bild wird dann z.B. sobald es benötigt wird mit einem Lazy Load nachgeladen.
    [Bild: AMD_Threadripper.png] Bei Hardware gibt es keine eigene Meinung, bei Hardware zählen nur die Fakten.


    Probleme mit der Haarpracht? Starres Haar ohne Glanz? TressFX schafft Abhilfe. Ja, TressFX verhilft auch Ihnen zu schönem und Geschmeidigen Haar.
    [Bild: i6tfHoa3ooSEraFH63.png]
    Multithread ist offline

  3. #3 Zitieren
    Ritter Avatar von Delta 38
    Registriert seit
    Nov 2008
    Ort
    Bremen
    Beiträge
    1.251
    Prinzipiell geht es um ein einfaches Abspeichern von Daten, ja.
    Ich möchte mir jedoch die Möglichkeit vorbehalten die Art und Weise, wie ich die Daten abspeichere (SQL, XML, Plain Text, etc.) nachher noch zu ändern und wollte das deshalb über ein Interface implementieren. Die Datenbank/PersistenceService ist sozusagen als Plugin für die Anwendung gedacht (eigene dll).
    Ähnlich wie man bei MVC oder MVP die Benutzerschnittstelle austauschen kann ohne den Rest zu verändern geht es aber hier wohl nicht, da die Daten sich auf die spezifischen Benutzeroberflächen beziehen und ich mit einem Wechsel dieser wohl auch das Interface ändern muss um die anderen Typen aufzunehmen.

    Edit: Hier nochmal das Konzept, welches ich gerne verfolgen möchte. Die einzelnen Komponenten (außer vielleicht EntityDomain) sollen so ausgetauscht werden, dass die anderen nicht angefasst werden müssen.
    Wahrscheinlich habe ich mich jetzt schon zu lange mit dem Problem beschäftigt und mich dabei irgendwie fest gefahren

    [Bild: concept.png]
    Delta 38 ist offline Geändert von Delta 38 (27.06.2018 um 21:28 Uhr)

  4. #4 Zitieren
    Pretty Pink Pony Princess  Avatar von Multithread
    Registriert seit
    Jun 2010
    Ort
    Crystal Empire
    Beiträge
    11.228
    Nichts einfacher als das

    Im Grunde ist es ja nur das Umsetzungskonzept, welches dir fehlt, die Datenstruktur sieht gut aus.


    Ich habe Folgende Typen:
    1. DataClass(PersistendStorageEnum inStorageType): Attribute
    2. DataField(string inName=null): Attribut
    3. public static class DataAccessHandler
    4. public abstract class RootSavableObject
    5. public class DataLoader<T>

    Die beiden Attribute sind dazu da, um Festzulegen in welches Storage Device die Daten abgelegt werden sollen, bzw. von Wo Sie ausgelesen werden können (DataClass). Und, falls das Feld anders heisen muss (zb. bei Fremddatenbanken), mit einem Namen versehende Properties (DataField).

    Die Statische Klasse DataAccessHelper hat im grossen und ganzen zwei Statische Methoden (SaveObject(RootSavableObject inObject) und LoadList<T>(Func<bool> inQuery)). Diese dienen dazu, Daten in einen Speicher abzulegen und wieder daraus zu lesen. Dort befindet sich danach auch der Grossteil deiner Logik. Im Späteren verlauf kann man hier auch am meisten verbessern, was Erweiterbarkeit und Dynamik angeht.

    Die Abstrakte Klasse ist die Basis für alle deine Klassen, welche du Persisten Speichern/Laden musst.
    Diese Kennt eine Hilfsmethode: Save(RightObject inRights =null).

    Auslesen tust du Daten über einen DataLoader. Am einfachsten geht es sicher, wenn du selber ein Objekt mit Filter und Sortierbedingungen erstellst, welche du Dynamisch setzen kannst. Richtig schön wird es aber erst, wenn du LINQ Expressions übergeben kannst, dann fragst du deinen Datenstorage beinahe gleich ab, wie du auch normalen Code schreiben würdest.
    Beispiel:
    Code:
    var tmpKontoWithMoney=new DataLoader<Konto>().Where(inKonto=> inKonto.User==CurrentUser && inKonto.Balance>0).FirstOrDefault();
    <T> ist ein Typ. Wird für Generics im C# verwendet.
    Welche Sprache verwendest du?

    Was das Grobkonzept angeht: Alles in allem ist das, was Ich oben aufzeige eine Zweilayer Architektur. Mit etwas Geschick, und Lambda, kannst du Sogar Unittests schreiben, welche deinen Code, inklusive 'DB Abfragen' über DataLoader testen kannst.
    [Bild: AMD_Threadripper.png] Bei Hardware gibt es keine eigene Meinung, bei Hardware zählen nur die Fakten.


    Probleme mit der Haarpracht? Starres Haar ohne Glanz? TressFX schafft Abhilfe. Ja, TressFX verhilft auch Ihnen zu schönem und Geschmeidigen Haar.
    [Bild: i6tfHoa3ooSEraFH63.png]
    Multithread ist offline

  5. #5 Zitieren
    Ritter Avatar von Delta 38
    Registriert seit
    Nov 2008
    Ort
    Bremen
    Beiträge
    1.251
    Danke für deine Antwort, das geht auf jeden Fall in die Richtung, die ich anpeile.
    Ich versuche das nochmal heute Abend umzusetzen, als Programmiersprache verwende ich C#.
    Delta 38 ist offline

  6. #6 Zitieren
    Ritter Avatar von Delta 38
    Registriert seit
    Nov 2008
    Ort
    Bremen
    Beiträge
    1.251
    Ich glaube ich habe das noch nicht so richtig verstanden.
    1. Erben meine Datenobjekte von RootSaveableObject?
    2. Was genau ist RightObject? Ich kann die Funktion ja nicht deklarieren ohne den Typ 'RightObject' (Datenobjekt, Datenobjekt mit Icon, Datenobjekt mit Ascii, ...) zu kennen, oder?
    3. Der DataAccessHandler würde dann inObject.Save(Datenobjekt_mit_Icon) aufrufen?
    4. Wie kann der DataAccessHelper eine generische Methode Load<T> haben? Er kann ja beim Laden keine Objekte erstellen, oder? Und wenn er das könnte, dann müssten ja beispielsweise all diese generischen Objekte eine gemeinsame Basis haben, um die eigenen Attribute mit den geladenen zu füllen.

    Sorry wenn ich da ein bisschen auf dem Schlauch stehe
    Delta 38 ist offline Geändert von Delta 38 (29.06.2018 um 17:34 Uhr)

  7. #7 Zitieren
    Ritter
    Registriert seit
    Feb 2003
    Beiträge
    1.554
    Zitat Zitat von Multithread Beitrag anzeigen
    Die beiden Attribute sind dazu da, um Festzulegen in welches Storage Device die Daten abgelegt werden sollen, bzw. von Wo Sie ausgelesen werden können (DataClass). Und, falls das Feld anders heisen muss (zb. bei Fremddatenbanken), mit einem Namen versehende Properties (DataField).
    Das ist ein äußerst schlechter Stil, da du das Datenobjekt von der Persistierung abhängig machst. Wozu muss auch das Datenobjekt wissen, wo es gespeichert wird? Das verstößt auch gegen das Single-Responsibility-Prinzip. Um nachher Unittests schreiben zu können, muss man dann die kompette Persistierung mocken. Das ist ein erheblicher Mehrwand.

    Zitat Zitat von Multithread Beitrag anzeigen
    Die Statische Klasse DataAccessHelper hat im grossen und ganzen zwei Statische Methoden (SaveObject(RootSavableObject inObject) und LoadList<T>(Func<bool> inQuery)). Diese dienen dazu, Daten in einen Speicher abzulegen und wieder daraus zu lesen. Dort befindet sich danach auch der Grossteil deiner Logik. Im Späteren verlauf kann man hier auch am meisten verbessern, was Erweiterbarkeit und Dynamik angeht.
    Statische Methoden, um ein Objekt von irgendwo schreiben/lesen zu können? Ebenfalls ein sehr schlechter Stil. Statische Methoden lassen sich nicht überschreiben, also muss er statische Methoden jeweils für eine SQL-Datenbank, eine XML-Datei, einer CSV-Datei, etc. schreiben

    Zitat Zitat von Multithread Beitrag anzeigen
    Die Abstrakte Klasse ist die Basis für alle deine Klassen, welche du Persisten Speichern/Laden musst.
    Diese Kennt eine Hilfsmethode: Save(RightObject inRights =null).
    Ein Datenobjekt, was selbst für die Persistierung sorgt, verstößt gegen das Single-Responsibility-Prinzip. Datenobjekte sollen so Dumm wie möglich sein und sollten keine Logik besitzen.

    Ich weiß auch nicht, wieso man es sich so schwer macht? Wieso muss im Datenobjekt "Konto" überhaupt das Icon gespeichert sein? Können es unterschiedliche Icons sein? Wenn ja, dann macht man daraus zwei Datenobjekte und verbindet beide über eine ID

    - Konto
    Code:
    Konto
      ID
      Name
      IBAN
      ...
    
    KontoIcon
      ID
      KontoID
      Icon
    Somit brauch dann die Konsolenanwendung nur Konto lesen und wenn die UI noch ein Logo benötigt, kann sie noch KontoIcon joinen. Fertig.
    Bezüglich der Persistierung: Schaue dir mal das Repository und das Unit-Of-Work-Pattern an.
    Whiz-zarD ist offline

  8. #8 Zitieren
    Pretty Pink Pony Princess  Avatar von Multithread
    Registriert seit
    Jun 2010
    Ort
    Crystal Empire
    Beiträge
    11.228
    Zitat Zitat von Delta 38 Beitrag anzeigen
    Ich glaube ich habe das noch nicht so richtig verstanden.
    1. Erben meine Datenobjekte von RootSaveableObject?
    2. Was genau ist RightObject? Ich kann die Funktion ja nicht deklarieren ohne den Typ 'RightObject' (Datenobjekt, Datenobjekt mit Icon, Datenobjekt mit Ascii, ...) zu kennen, oder?
    3. Der DataAccessHandler würde dann inObject.Save(Datenobjekt_mit_Icon) aufrufen?
    4. Wie kann der DataAccessHelper eine generische Methode Load<T> haben? Er kann ja beim Laden keine Objekte erstellen, oder? Und wenn er das könnte, dann müssten ja beispielsweise all diese generischen Objekte eine gemeinsame Basis haben, um die eigenen Attribute mit den geladenen zu füllen.
    1. Genau.
    2. Damit wären Rechte gemeint. zb. die Rechte vom aktuellen Benutzer. Für deinen Fall kann man das auch weglassen.
    3. Nein. Der DataAccessHandler schaut sich das Objekt an (per Reflection) Und speichert es danach Persistent ab. Dafür werden die beiden Attribute benötigt. Das auf der Klasse gibt den Tabellennamen an und die auf den Properties geben an welche werte gespeichert werden sollen. Diese Informationen kann man per Reflection aus jedem beliebigen Objekt holen.
    4. Reflection. Du kannst im C# ein beliebiges Objekt erstellen, wenn du nur den Typ kennst. Und der Parameter T ist der Typ.
    [code](T)Activator.CreateInstance(typeof(T));[code]
    Nachdem das Objekt erstellt wurde, kann es mit den Informationen aus deinem Persistenten Speicher abgefüllt werden. Dies lässt sich auch wieder über Reflection lösen.

    Als generischer Einstiegt kann in dem Fall auch die Microsoft Dokumentation helfen. Dort ist alles beschrieben was du kannst. knapp die Hälfte der genanten Informationen brauchst du für dieses Projekt.

    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    Das ist ein äußerst schlechter Stil, da du das Datenobjekt von der Persistierung abhängig machst. Wozu muss auch das Datenobjekt wissen, wo es gespeichert wird? Das verstößt auch gegen das Single-Responsibility-Prinzip. Um nachher Unittests schreiben zu können, muss man dann die kompette Persistierung mocken. Das ist ein erheblicher Mehrwand.
    Stimmt, wo gehört da nicht rein. Mit wo wollte ich eigentlich sagen das im Klassenattribut sowas drinstehen kann wie:
    Tabellenname, Fulltextindex, Relationale DB, NoSQl Db...

    Aber nicht:
    MS-SQL,MySQL, MongoDB, Lucene,....

    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    Statische Methoden, um ein Objekt von irgendwo schreiben/lesen zu können? Ebenfalls ein sehr schlechter Stil. Statische Methoden lassen sich nicht überschreiben, also muss er statische Methoden jeweils für eine SQL-Datenbank, eine XML-Datei, einer CSV-Datei, etc. schreiben
    Eben nicht. Die Statische Methode ist nur dazu da, um alle Zugriffe am ende über Klassen eines Interfaces laufen zu lassen.
    Jeder Speicher/Lesezugriff soll über diese ggf. Multithreadingfähige statische Funktion laufen. Diese entscheidet dann anhand des Objektes und den verfügbaren Datenspeicherorten wo und wie die Klasse abgelegt werden soll.

    Wenn es um das SRP geht:
    Code:
    DataHelper.Save(YourObject);
    Das kommt damit am ende fast aufs gleiche raus.
    Mit einem unterschied: wenn die Speichermethode IN der Klasse aufgerufen wird, kann die Klasse beim Speichern noch bestimmte dinge tun.
    Da sollte Sie zwar nicht, aber in der Freien Wildbahn macht dies durchaus sinn.

    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    Ich weiß auch nicht, wieso man es sich so schwer macht? Wieso muss im Datenobjekt "Konto" überhaupt das Icon gespeichert sein? Können es unterschiedliche Icons sein? Wenn ja, dann macht man daraus zwei Datenobjekte und verbindet beide über eine ID
    ....
    Da stimme Ich mit dir ganz überein.
    Ich denke aber das Persistente Speichern der Daten hat vorrang, das andere kann man Später noch korrigieren, oder beim nächsten mal besser machen.


    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    Somit brauch dann die Konsolenanwendung nur Konto lesen und wenn die UI noch ein Logo benötigt, kann sie noch KontoIcon joinen. Fertig.
    Bezüglich der Persistierung: Schaue dir mal das Repository und das Unit-Of-Work-Pattern an.
    Das klingt für mich nach einem Factory Pattern.
    Kann man anwenden, muss man aber nicht.

    Ich nehme an du arbeitest auch in der Softwarentwicklung oder hast dies zumindest gelernt.
    Viele dieser 'Regeln' kann man nicht zu 100% Durchsetzen ohne wiederum Code-smell zu verursachen (zb. hunderte Klassen welche ähnlichen Code ausführen, 'Interessante' Formatierungen, Unleserlichen Code).
    Ich benutze einige dieser Regeln auch, aber nur mit diesen Regeln schreibt man keinen guten Code. Guten Code schreibt man mMn. mit Verständnis und sinnvollen Namen.
    [Bild: AMD_Threadripper.png] Bei Hardware gibt es keine eigene Meinung, bei Hardware zählen nur die Fakten.


    Probleme mit der Haarpracht? Starres Haar ohne Glanz? TressFX schafft Abhilfe. Ja, TressFX verhilft auch Ihnen zu schönem und Geschmeidigen Haar.
    [Bild: i6tfHoa3ooSEraFH63.png]
    Multithread ist offline Geändert von Multithread (01.07.2018 um 22:03 Uhr)

  9. #9 Zitieren
    Ritter
    Registriert seit
    Feb 2003
    Beiträge
    1.554
    Zitat Zitat von Multithread Beitrag anzeigen
    Stimmt, wo gehört da nicht rein. Mit wo wollte ich eigentlich sagen das im Klassenattribut sowas drinstehen kann wie:
    Tabellenname, Fulltextindex, Relationale DB, NoSQl Db...
    Auch sowas sollte nicht in den Datenobjekten stehen. Das Datenobjekt sollte nichts über seine Persistierung wissen.
    Sowas, wie Tabellenname sollte über ein Mapping erfolgen. C# unterstützt zwar Attribute, die man per Reflektion auslesen kann aber solche Attribute sollte man mit Vorsicht genießen.

    Zitat Zitat von Multithread Beitrag anzeigen
    Eben nicht. Die Statische Methode ist nur dazu da, um alle Zugriffe am ende über Klassen eines Interfaces laufen zu lassen.
    Jeder Speicher/Lesezugriff soll über diese ggf. Multithreadingfähige statische Funktion laufen. Diese entscheidet dann anhand des Objektes und den verfügbaren Datenspeicherorten wo und wie die Klasse abgelegt werden soll.
    Und welchen Mehrwert hast du nun davon? Im Grunde hast du damit nur Nachteile, weil du dich von der statischen Klasse abhängig machst, die du dann nicht mocken kannst. Zumal kannst du dann nur eine einzige Datenquelle angeben, da statische Klassen nicht instanziierbar sind. Du kannst also nicht gleichzeitig von XML-Dateien und SQL-Datenbanken lesen, da die statische Klasse bestimmt, was die Datenquelle ist. Auch kannst du hier plötzliche unvorhersehbare Seiteneffekte verursachen, wenn du während der Laufzeit zulässt, die Datenquelle zu ändern.

    Von statischen Klassen sollte man generell Abstand nehmen. Wenn eine Klasse statisch ist, dann sollte man die Finger von der Tastatur nehmen und überlegen, warum sie eigentlich statisch sein muss und ob es nicht bessere Lösungen gibt? Wie gesagt, sich von statischen Klassen abhängig machen, ist für spätere Unittests Gift.

    Wenn man es richtig machen will, sollte man ein Interface schreiben und dann das Interface implementieren.

    Code:
    IUnitOfWork
      Register(Object object);
      Save();
    Und dann implementierst du dieses Interface
    Code:
    XmlUnitofWork : IunitOfWork
    MsSqlUnitofWork : IUnitOfWork
    ...
    Das selbe gilt dann auch für Repositories.

    Viele dieser 'Regeln' kann man nicht zu 100% Durchsetzen ohne wiederum Code-smell zu verursachen (zb. hunderte Klassen welche ähnlichen Code ausführen, 'Interessante' Formatierungen, Unleserlichen Code).
    Ich benutze einige dieser Regeln auch, aber nur mit diesen Regeln schreibt man keinen guten Code. Guten Code schreibt man mMn. mit Verständnis und sinnvollen Namen.
    Wir reden aber hier um ein neues Projekt und da sollte man Codesmells vermeiden und ja, ich arbeite jetzt seit 6 Jahren beruflich als Softwareentwickler und ich habe schon ziemlich viel scheiße gesehen.
    Whiz-zarD ist offline

  10. #10 Zitieren
    Pretty Pink Pony Princess  Avatar von Multithread
    Registriert seit
    Jun 2010
    Ort
    Crystal Empire
    Beiträge
    11.228
    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    Auch sowas sollte nicht in den Datenobjekten stehen. Das Datenobjekt sollte nichts über seine Persistierung wissen.
    Sowas, wie Tabellenname sollte über ein Mapping erfolgen. C# unterstützt zwar Attribute, die man per Reflektion auslesen kann aber solche Attribute sollte man mit Vorsicht genießen.
    Entweder weiss es das Objekt, oder eine Statische Factory Klasse.
    Etwas anderes, welches ohne grossen Mehraufwand verwendet werden kann, habe Ich bisher noch nicht gesehen.

    Meiner Meinung nach, sollte das Objekt alles wissen, was direkt mit Ihm in Verbindung steht. Aber nicht mehr. Auch die aufteilung nach Model (Alles was mit dem Speichern und der Datenhaltung zu tun hat) und View (abgeleitet vom Model, beinhaltet alles was für ein GUI nötig ist, zb. das Icon Property)
    In dem Fall von oben zb. ist das Icon zwar als Property vorhanden, wird aber aus einer anderen Quelle Geladen bei der ersten Abfrage.

    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    Und welchen Mehrwert hast du nun davon? Im Grunde hast du damit nur Nachteile, weil du dich von der statischen Klasse abhängig machst, die du dann nicht mocken kannst. Zumal kannst du dann nur eine einzige Datenquelle angeben, da statische Klassen nicht instanziierbar sind. Du kannst also nicht gleichzeitig von XML-Dateien und SQL-Datenbanken lesen, da die statische Klasse bestimmt, was die Datenquelle ist. Auch kannst du hier plötzliche unvorhersehbare Seiteneffekte verursachen, wenn du während der Laufzeit zulässt, die Datenquelle zu ändern.

    Von statischen Klassen sollte man generell Abstand nehmen. Wenn eine Klasse statisch ist, dann sollte man die Finger von der Tastatur nehmen und überlegen, warum sie eigentlich statisch sein muss und ob es nicht bessere Lösungen gibt? Wie gesagt, sich von statischen Klassen abhängig machen, ist für spätere Unittests Gift.
    Die Statische Metode Speicher nicht.
    Die Statische Methode geht nur die Liste registrierter 'IPersistentStorage' Instanzen durch, und Prüft mit welcher das Objekt geschrieben /gelesen werden soll.
    Es besteht damit auch die Möglichkeit ein Objekt in mehreren Speichern abzulegen (SQL DB und zb. Lucene für eine Effiziente Volltextsuche) und beim Lesen zu entscheiden woher gelesen werden soll.

    Die Registrierung erfolgt analog einer DependencyIncection.

    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    Wir reden aber hier um ein neues Projekt und da sollte man Codesmells vermeiden und ja, ich arbeite jetzt seit 6 Jahren beruflich als Softwareentwickler und ich habe schon ziemlich viel scheiße gesehen.
    Dann haben wir auch etwa gleich viel Erfahrung. Ich bin jetzt seit 5 Jahren dabei.
    Das bisher grösste Problem, welches Ich gesehen habe, ist das Fehlen eines Designs/einer Linie welcher man beim Programmieren folgen kann.

    Ich würde gerne sehen wie du das Implementieren würdest. In meinem Kopf habe Ich dann doppelten Code, aber vermutlich übersehe Ich nur was.
    Mit ISavableObject als BasisInterface für alles was sich Speichern lässt.

    PS: Statische Methoden würde Ich wegen den Unittests nicht verteufeln.
    Ua. gibt es Funktionen die man gut in einer Statischen Methode abbilden kann, welche nur die Eingabeparameter braucht (z.B. Irgendwelche Berechnungen welche auch mal mehrere 100 Zeilen lang sind)
    Und der Statische Kontext ist auch nicht automatisch des Teufels. Ich arbeite viel auch mit Caches, da eine DB Abfrage mit 0.3-2ms für gewisse dinge viel zu langsam ist. Und Caches sind auch alle Statisch.
    Sofern man den Cache nach einem Unittest sauber aufräumt (und dafür gibts ja ein Test-Teardown), können diese problemlos mitgetestet werden. Wenn man es nicht macht, dann gibt das die schönsten Effekte[Bild: 006.gif]

    Unsere Datenbank Integrationstests sind da so ein Problem. Diese müssen Testklasse für Testklasse durchlaufen da sie sonst Fehler werden. bzw. mit Methode für Methode wären Sie viel zu Langsam.
    [Bild: AMD_Threadripper.png] Bei Hardware gibt es keine eigene Meinung, bei Hardware zählen nur die Fakten.


    Probleme mit der Haarpracht? Starres Haar ohne Glanz? TressFX schafft Abhilfe. Ja, TressFX verhilft auch Ihnen zu schönem und Geschmeidigen Haar.
    [Bild: i6tfHoa3ooSEraFH63.png]
    Multithread ist offline

  11. #11 Zitieren
    Ritter Avatar von Delta 38
    Registriert seit
    Nov 2008
    Ort
    Bremen
    Beiträge
    1.251
    Erstmal vielen Dank euch beiden! Ich werde versuchen beide Methoden zu implementieren und mir dann eine aussuchen :-D Allerdings kann ich die nächsten 1-3 Wochen nicht daran weiterarbeiten.
    Delta 38 ist offline

  12. #12 Zitieren
    Ritter
    Registriert seit
    Feb 2003
    Beiträge
    1.554
    Zitat Zitat von Multithread Beitrag anzeigen
    Entweder weiss es das Objekt, oder eine Statische Factory Klasse.
    Etwas anderes, welches ohne grossen Mehraufwand verwendet werden kann, habe Ich bisher noch nicht gesehen.
    Dann schaue dir z.B. gängige O/R-Mapper an. z.B. Entity Framework Core:
    Code:
    public partial class Student
    {
        public int StudentId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    Code:
    public partial class SchoolDBContext : DbContext
    {
        public virtual DbSet<Student> Student { get; set; }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Student>(entity =>
            {
                entity.Property(e => e.StudentId).HasColumnName("StudentID");
    
                entity.Property(e => e.FirstName)
                    .HasMaxLength(50)
                    .IsUnicode(false);
    
                entity.Property(e => e.LastName)
                    .HasMaxLength(50)
                    .IsUnicode(false);
            });
        }
    }
    (N)Hibernate macht dies über eine XML-Datei:
    Code:
    <?xml version = "1.0" encoding = "utf-8" ?> 
    
    <hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" 
       assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp">
    
       <class name = "Student"> 
          <id name = "StudentId">
            <generator class = "native"/> 
          </id> 
    		
          <property name = "FirstName"/> 
          <property name = "LastName"/> 
       </class> 
       
    </hibernate-mapping>
    Oder über das AddOn FluentNHibernate typsicher über Code:
    Code:
    public class StudentMap : ClassMap<Student>
    {
     public StudentMap()
     {
         Id(x => x.StudentId);
    
         Map(x => x.FirstName)
             .Length(50)
             .Not.Nullable();
    
         Map(x => x.LastName)
             .Length(50)
             .Not.Nullable();
    }
    Ein anderes Beispiel wäre der CsvHelper, um csv-Dateien einzulesen:
    Code:
    public class StudentMap : ClassMap<Student>
    {
        public StudentMap()
        {
            Map(m => m.StudentId).Name("STUDENT_ID");
            Map(m => m.FirstName).Name("FIRSTNAME");
            Map(m => m.LastName).Name("LASTNAME");
        }
    }
    Die einzige Technik, die ich kenne, wo in den Klassen die Mapping-Informationen stehen, ist der XmlSerializer aus dem .Net-Framework aber will man ernsthaft seine Datenklassen mit (De-)serialisierungsattributen zumüllen?

    Wenn eine Klasse Daten und dann noch zusätzlich Informationen über die Persistierung beinhaltet, verstößt die Klasse gegen das Single-Responsibility-Prinzip, da sie zwei Aufgaben beinhaltet.

    Die Statische Metode Speicher nicht.
    Die Statische Methode geht nur die Liste registrierter 'IPersistentStorage' Instanzen durch, und Prüft mit welcher das Objekt geschrieben /gelesen werden soll.
    Es besteht damit auch die Möglichkeit ein Objekt in mehreren Speichern abzulegen (SQL DB und zb. Lucene für eine Effiziente Volltextsuche) und beim Lesen zu entscheiden woher gelesen werden soll.

    Die Registrierung erfolgt analog einer DependencyIncection.
    Statische Klassen haben kein Konstruktor und somit kannst du auch nicht mit Dependency Injection arbeiten. Das, was du wohl meinst, sind Objekte, die statisch gehalten werden (Singleton-Pattern). Objekte, die statisch gehalten werden und statische Klassen sind zwei unterschiedliche paar Schuhe.

    Das bisher grösste Problem, welches Ich gesehen habe, ist das Fehlen eines Designs/einer Linie welcher man beim Programmieren folgen kann.
    Das größte Problem, was ich gesehen habe, sind statische Klassen. Ich arbeite hier ein einer Firma, in der vor über 10 Jahren ein O/R-Mapper selbstgebaut wurde und der immer noch verwendet wird. Hier hat man tatsächlich statische Klassen implementiert, um z.B. die Datenbank zu lesen. Die Folge daraus ist nun, dass die komplette Anwendung nicht mit Unittests getestet werden kann und auch kein Multithreading möglich ist, da aufgrund der statischen Klassen zu viele Seiteneffekte entstehen.

    Ich würde gerne sehen wie du das Implementieren würdest. In meinem Kopf habe Ich dann doppelten Code, aber vermutlich übersehe Ich nur was.
    Mit ISavableObject als BasisInterface für alles was sich Speichern lässt.
    Über das Repository- und Unit-of-Work-Pattern, auf das ebenfalls gängige O/R-Mapper basieren. Um bei C# zu bleiben hätte ich dann nachher ein Projekt (eine dll-Datei) mit den Repositories vom Entity Framework, ein weiteres Projekt für NHibernate. etc...

    PS: Statische Methoden würde Ich wegen den Unittests nicht verteufeln.
    Eben doch. Methoden, die statische Methoden aufrufen, lassen sich nicht isoliert testen. Ein Klassiker unter den Beispielen wäre DateTime.Now(). Habe ich jetzt eine Methode, die DateTime.Now() aufruft, dann erzeugt die Methode immer wieder ein anderes Resultat und lässt sich somit u.U. nicht mehr testen. DateTime.Now() kann ich auch nicht mocken.

    Ua. gibt es Funktionen die man gut in einer Statischen Methode abbilden kann, welche nur die Eingabeparameter braucht (z.B. Irgendwelche Berechnungen welche auch mal mehrere 100 Zeilen lang sind)
    Wenn man Methoden schreibt, die 100 Zeilen lang sind, dann macht man grundsätzlich was verkehrt. Ich rede hier aus Erfahrung, da ich ständig mit solchen Code zu tun habe.

    Sofern man den Cache nach einem Unittest sauber aufräumt (und dafür gibts ja ein Test-Teardown), können diese problemlos mitgetestet werden. Wenn man es nicht macht, dann gibt das die schönsten Effekte
    Und das ist der nächste Nachteil von statischen Methoden. Sie können Seiteneffekte hervorrufen, wenn sie nicht stateless sind. Caches, die statisch sind, würde ich immer vermeiden. Das Problem sah ich auch in unserer Anwendung. Da waren auch einige Caches statisch, was zur Folge hatte, ruft man eine Methode zwei Mal auf, gibt die Methode zwei unterschiedliche Ergebnisse zurück, weil der Cache nicht aufgeräumt wurde. Wir haben einizige der Caches instanziierbar gemacht und konnten auch Haufenweise Code rausschmeißen, weil er immer den Status des Caches überprüft hat.

    Ein statischer Cache ist auch nicht Multithreading-tauglich. Wenn plötzlich 10 Threads den Cache verwenden wollen, ist das eher hinderlich als nützlich, wenn der Zugriff jedes Mal blockiert werden muss. Da sollte jeder Thread seinen eigenen Cache verwenden. Manchmal kommt man zwar nicht drumherum, dass Threads auf gemeinsame Daten zurückgreifen müssen aber diese Daten würde ich ebenfalls nicht statisch machen.

    Statische Klassen haben auch das Problem, dass sie zu einer Müllhalde von Methoden werden, von denen man nicht weiß, wohin damit. Solche Klassen heißen dann oft Helper oder Utils. Solche Klassen habe ich hier auch zu Hauf und beinhalten schon Methoden im dreistelligen Bereich und werden in der gesamten Anwendung verteilt genutzt.

    Alles in einem verursachen also statische Klassen nur Probleme.
    Whiz-zarD ist offline Geändert von Whiz-zarD (03.07.2018 um 09:11 Uhr)

  13. #13 Zitieren
    Ritter Avatar von Delta 38
    Registriert seit
    Nov 2008
    Ort
    Bremen
    Beiträge
    1.251
    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    [...]Wenn man es richtig machen will, sollte man ein Interface schreiben und dann das Interface implementieren.

    Code:
    IUnitOfWork
      Register(Object object);
      Save();
    Und dann implementierst du dieses Interface
    Code:
    XmlUnitofWork : IunitOfWork
    MsSqlUnitofWork : IUnitOfWork
    ...
    Das selbe gilt dann auch für Repositories.[...]
    Das bedeutet, dass ich für jede neue View eine neue UnitOfWork schreiben muss, richtig? Jede View wird potentiell ja andere Repositories verwenden (die gefallen mir übrigens sehr gut!). Die Konsolenanwendung verwendet dabei das KontoRepository, während die grafische Anwendung das KontoViewItemRepository verwendet. Entsprechend muss ich also Persistence-Layer und Views immer gleichzeitig austauschen, richtig? Oder eben erweitern, so dass die UnitOfWork neben dem KontoRepository zusätzlich noch ein KontoIconRepository hat.

    Das Projekt steckt noch ziemlich in den Kinderschuhen, ich kann mir deshalb vorstellen, dass die Oberfläche in einem späteren Release gegen eine wesentlich umfangreichere eingetauscht wird. Dafür möchte ich jetzt schon den Weg bereiten, sodass man wenn möglich einfach nur eine Fabrik ändern muss, die Views erstellt
    Vielleicht ist das aber auch nicht machbar.
    Delta 38 ist offline

  14. #14 Zitieren
    Pretty Pink Pony Princess  Avatar von Multithread
    Registriert seit
    Jun 2010
    Ort
    Crystal Empire
    Beiträge
    11.228
    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    Dann schaue dir z.B. gängige O/R-Mapper an. z.B. Entity Framework Core:
    Das könnten wir für Objekte, welche wir Importieren/Exportieren so machen.
    Könnte durchaus etwas arbeit abnehmen.


    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    Die einzige Technik, die ich kenne, wo in den Klassen die Mapping-Informationen stehen, ist der XmlSerializer aus dem .Net-Framework aber will man ernsthaft seine Datenklassen mit (De-)serialisierungsattributen zumüllen?
    Die Technologie, welche wir aktuell verwenden, basiert Teilweise darauf, zumindest vom Prinzip her.
    Ich sehe den Nachteil, auf welchen du Hinaus möchtest. Wir haben das mit noch mehr Properties angefügt.

    Mal mit Cheffe reden wie wir das mit der Lucene Suche machen, vielleicht kann man da noch was drehen

    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    Statische Klassen haben kein Konstruktor und somit kannst du auch nicht mit Dependency Injection arbeiten. Das, was du wohl meinst, sind Objekte, die statisch gehalten werden (Singleton-Pattern). Objekte, die statisch gehalten werden und statische Klassen sind zwei unterschiedliche paar Schuhe.
    Die Statische Klasse wird ja auch nicht Injected. Dependency Injection arbeitet intern mit einer Statischen Klasse, welche ua ein Dictionary mit <Type,Type> beinhaltet. Da ist das ganze Mapping drin.
    Und dieses Statische Mapping kann man für Unittests ebenfalls mocken.
    Deshalb kann Ich meine 'Statischen' Datenbankabfragen ebenfalls mocken und damit selbst Statische Methoden Testen, welche Daten von einer Datenquelle lesen und danach zurückgeben.

    Siehe Beispiel 1.

    Zitat Zitat von Whiz-zarD Beitrag anzeigen

    Das größte Problem, was ich gesehen habe, sind statische Klassen. Ich arbeite hier ein einer Firma, in der vor über 10 Jahren ein O/R-Mapper selbstgebaut wurde und der immer noch verwendet wird. Hier hat man tatsächlich statische Klassen implementiert, um z.B. die Datenbank zu lesen. Die Folge daraus ist nun, dass die komplette Anwendung nicht mit Unittests getestet werden kann und auch kein Multithreading möglich ist, da aufgrund der statischen Klassen zu viele Seiteneffekte entstehen.
    Multithreading und Unittests sind zwei dinge. Es ist möglich das Klassen nicht Multithreaded getested werden können, aufgrund der Seiteneffekte (Ich kann trotzdem mehrere Instanzen Parallel Testen (eigener Pool).
    Multithreading und dessen Probleme werden oftmals durch unwissen verursacht.

    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    Und das ist der nächste Nachteil von statischen Methoden. Sie können Seiteneffekte hervorrufen, wenn sie nicht stateless sind. Caches, die statisch sind, würde ich immer vermeiden. Das Problem sah ich auch in unserer Anwendung. Da waren auch einige Caches statisch, was zur Folge hatte, ruft man eine Methode zwei Mal auf, gibt die Methode zwei unterschiedliche Ergebnisse zurück, weil der Cache nicht aufgeräumt wurde. Wir haben einizige der Caches instanziierbar gemacht und konnten auch Haufenweise Code rausschmeißen, weil er immer den Status des Caches überprüft hat.
    C# hat dafür ua. ConcurrencyList und ConcurrencyDictionary, welche dir diese Problem mit Write-race abhandeln.

    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    Ein statischer Cache ist auch nicht Multithreading-tauglich. Wenn plötzlich 10 Threads den Cache verwenden wollen, ist das eher hinderlich als nützlich, wenn der Zugriff jedes Mal blockiert werden muss. Da sollte jeder Thread seinen eigenen Cache verwenden. Manchmal kommt man zwar nicht drumherum, dass Threads auf gemeinsame Daten zurückgreifen müssen aber diese Daten würde ich ebenfalls nicht statisch machen.
    Der Zugriff auf einen Cache muss nicht Blockiert werden.
    Je nach Programmierung nicht einmal zum schreiben. (z.B. Database Cluster, wenn du von zwei Datenbanken gleichzeitig liest, kann es sein, das du unterschiedliche Resultate bekommst, ohne das dies zur Laufzeit ein Problem ist.


    Zitat Zitat von Whiz-zarD Beitrag anzeigen
    Statische Klassen haben auch das Problem, dass sie zu einer Müllhalde von Methoden werden, von denen man nicht weiß, wohin damit. Solche Klassen heißen dann oft Helper oder Utils. Solche Klassen habe ich hier auch zu Hauf und beinhalten schon Methoden im dreistelligen Bereich und werden in der gesamten Anwendung verteilt genutzt.

    Alles in einem verursachen also statische Klassen nur Probleme.
    Dies Helper klassen kenne Ich.
    Bei uns ist aber klar geregelt, was in einen Helper darf/soll und was nicht.
    Im Helper sind hauptsächliche Funktionen, welche Spezifische Datenpackete der entsprechenden Klasse laden (Aktive, bestimter Status, mit Berechtigungen,..)
    Oder längere Berechnungsmethoden, welche dorthin ausgelagert wurden. Auch wieder: immer nur in den Helper der entsprechenden Klasse.

    Was das andere angeht: Statische Methoden haben durchaus Vorteile. Wenn man weiss, wie man mit ihnen umgehen muss.

    Wie in Beispiel 1: Man übergibt der Statischen Methode noch als Parameter 'DataCollection<Requirement> inCol =null' und verwendet diese anstelle der generierung einer neuen -> Klasse kann gemoqt und die methode getestet werden.


    Wo Ist bei den ganzen Mappern der DB-Access drin?
    Das Handhaben der Spalten/Daten hast du abgedeckt, aber den effektiven Access sehe Ich noch nicht.

    Beispiel 1:
    Spoiler:(zum lesen bitte Text markieren)

    Statische Methode, welche von einer Quelle Liest und Filtert:
    Rückgabe ist eine Data Storage abfrage Klasse, welche um weitere Filter ergänzt werden kann.
    Code:
     public static DataCollection<Requirement> GetAllActiiveRequirements()
            {
                var tmpCol = new DataCollection<Requirement>();
                tmpCol.Where<Requirement>(inItem=> inItem.IsActive && !inItem.IsDeleted);
                return tmpCol;
            }
    Statischer Daten Lader:
    Code:
     private static List<IPersistStorage> _registeredStorages;
            public static List<IDataObject> Load(IDataCollection inQuery)
            {
                foreach(var tmpStrorage in _registeredStorages)
                {
                    if (tmpStrorage.CanLoad(inQuery))
                    {
                        return tmpStrorage.GetData(inQuery);
                    }
                }
            }
            public static void AddPersistentStorage(IPersistStorage instorage)
            {
                _registeredStorages.Add(IPersistStorage);
            }
            public static void RemovePersistentStorage(IPersistStorage instorage)
            {
                _registeredStorages.Remove(IPersistStorage);
            }
    Und damit sind wir ganz am ende wieder bei nicht Statischen Objekten, welche wir Global verwenden, welche wir wiederum mit Unittests testen können.

    [Bild: AMD_Threadripper.png] Bei Hardware gibt es keine eigene Meinung, bei Hardware zählen nur die Fakten.


    Probleme mit der Haarpracht? Starres Haar ohne Glanz? TressFX schafft Abhilfe. Ja, TressFX verhilft auch Ihnen zu schönem und Geschmeidigen Haar.
    [Bild: i6tfHoa3ooSEraFH63.png]
    Multithread ist offline

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •