Laden...

Generischer Singleton erstellt keine unterschiedlichen Instanzen je nach Typ

Erstellt von Ornithopter vor 7 Jahren Letzter Beitrag vor 7 Jahren 3.137 Views
O
Ornithopter Themenstarter:in
9 Beiträge seit 2017
vor 7 Jahren
Generischer Singleton erstellt keine unterschiedlichen Instanzen je nach Typ

Zu den Stichworten „generic singleton“ und „generischer singleton“ habe ich leider keine vielversprechenden Titel gefunden.

Um unnötiges Laden bereits vorhandener Daten aus der Datenbank zu vermeiden, erfolgt der Zugriff auf Daten über einen Singleton, bei dem die entsprechende DataTable nur dann null ist, wenn sie noch nie zuvor geladen wurde oder wenn sie geändert wurde und daher neu geladen werden soll.

Für die verschiedenen in der Datenbank gespeicherten Typen (die von DataBaseEntityType erben) kann der Singleton generisch verwendet werden und enthält den Typen, für den er bestimmt ist, als Feld:


        protected T entityType;

Über dieses Feld können Methoden aufgerufen werden, die die genaue Unterklasse von DataBaseEntityType kennen.

Wenn ich zuerst über


    public class PropertyType : DataBaseEntityType

und dann über


    public class InventaryObjectType : ObjectType

(wobei ObjectType : DataBaseEntityType) auf


    public class Singleton<T> where T : DataBaseEntityType

zugreife (also Singleton<PropertyType> und später Singleton<InventaryObjectType), dann enthält entityType auch beim zweiten Fall PropertyType, obwohl es mit Singleton<InventaryObjectType> aufgerufen werde.

Dies liegt daran, dass beim ersten Aufruf von Singleton<InventaryObjectType>
instance != null
ist. Dies liegt vermutlich daran, dass zuvor Singleton<PropertyType> aufgerufen wurde.

  1. Kann das sein? Und wenn ja, wie kann das sein? Und wo liegt der Denkfehler?

Schließlich sind die beiden Typen nicht miteinander kompatibel, PropertyType erbt nicht von InventaryObjectType und umgekehrt, sie erben nur gemeinsam von DataBaseEntityType.

  1. Sollte man die Thematik besser komplett anders programmieren?

    public class Singleton<T> where T : DataBaseEntityType
    {
        protected T entityType;

        // Begin Singleton Pattern
        protected static Singleton<T> instance;

        protected Singleton(T value)
        {
            entityType = value;
        }

        public static Singleton<T> Instance(T value)
        {
            if (instance == null)
            {
                Console.WriteLine("instance == null");
                instance = new Singleton<T>(value);
            }

            PrintValueAndEntityType(value);

            return instance;
        }

        // End Singleton Pattern

        private static void PrintValueAndEntityType(T value)
        {
            Console.WriteLine();
            Console.WriteLine("Singleton<T> Instance(T value)");
            Console.WriteLine("value   instance.entityType");
            DataBaseEntityType[] entities = { value, instance.entityType };
            DataBaseEntityType.PrintLn(entities);
        }

T
2.219 Beiträge seit 2008
vor 7 Jahren

Ohne zu 100% sicher zu sein, da ich Singletons nie als Generic implementiert habe, würde ich static als Schuldigen im Verdacht sehen.

Ich würde hier eher pro Singleton eine eigene Klasse für das Singleton anlegen um die Typen auch wirklich getrennt zu haben.
Ich hatte mal vor einigen Jahren einn nicht static Singleton in C# gesehen.
Der könnte ggf. dann als Generic Nutzbar sein.

Nachtrag:
Wenn ich das Problem richtig verstehe, wollt ihr lediglich die Daten auf Serverseite durch euer Singleton cachen?
Falls ja, wäre das Generic Singleton nicht umbedingt der beste Ansatz.
Hier musst du es auch noch Thread Safe arbeiten, damit parallele Abfragen nicht zu X Ladeanfragen gegen die DB führen.
Im Umkehrschluss hast du in deinem Generic Singleton dann aber noch das Problem, wenn du nur ein Lock Objekt hast.
Dann locken sich wegen dem Generic Singleton auch alle anderen Singletons gegenseitig weg.
Dann steht deine CPU irgendwann auf 100% und deine Anwendung reagiert nicht mehr weil sich die Anfragen blockieren.

Hier stellt sich die Frage ob eine Cache Container Klasse, also ein Container für eure Klassen Objekte nicht besser wäre.
Ein DataTable als Cache zu verwenden produziert enormen Overhead und frisst auch viel mehr Speicher als nötig.

Wenn ihr Speicher schonend arbeiten wollt, dann nehmt einen Cache Container der als Generic alle Typen supportet.
Das Caching könnt ihr dann zwischen der Anwendungs- und der Datenschicht schieben.
Je nachdem wann eure Daten aus dem Cache geschoben werden sollen, müsst ihr dann entweder den Container nullen oder einfach dem Container sagen, dass er geleert werden soll.

Hier könnte dann jeder Container ggf. sein eigenes Lock Objekt mitbringen, damit ihr kein globale Lock habt.
Natürlich muss der Cache Container dann wiederum in einem Singleton für den Cache Container Typen gehandelt werden.

Nachtrag 2:

Hier wäre mein Denkansatz.
Ist natürlich nur schnell runter getippt und muss noch ausgebaut werden, ggf. gibt es sogar schon eine bekannte Alternative.
Aber so im groben würde ich mir einen generischen Cache Container im Ansatz vorstellen.


public class CacheContainer<T>
{	
	// Interne Cache Liste um die Objekte zwischen zu speichern!
	// Alternativ ein Dictionary mit Key -> PK + Value = Objekt
	private List<T> cache = new List<T>();
	
	public List<T> Cache
	{
		// Nur setter implementieren, da über Get sonst der Cache ohne Informationen von aussen geändert werden kann!
		set
		{
			// Fall der Cache nicht mehrfach gesetzt werden darf, könnte man hier noch eine entsprechend Exception werfen!
			
			// Wenn wir den Cache über Set erhalten, setzen wir auch cacheFilled da wir die Daten nun haben!
			this.cache = value;
			this.isCacheFilled = true;
		}
	}
	
	private bool isCacheFilled = false;
	
	// Helper Eigenschaft um zu prüfen ob der Cache ggf. noch gefüllt werden muss
	public bool IsCacheFilled
	{
		get
		{
			return this.isCacheFilled;
		}
	}
	
	// Methode zum leeren der internen Liste
	public void ClearCache()
	{
		// Interne Liste leeren und Flag wieder auf false setzen!
		this.cache.Clear();
		this.isCacheFilled = false;
	}
}

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

849 Beiträge seit 2006
vor 7 Jahren

Hallo Ornithopter,

also ich konnte jetzt beim überfliegen nichts sehen, was dein Verhalten erzeugen könnte.
Zumal ja zur compile Zeit aus den Generischen Typen Konkrete Typen werden.

Ich Habs mal ausprobiert, und es hat so funktioniert wie erwartet. Obwohl ich es komisch finde das Instance als Methode mit einem Parameter implementiert ist, der nur beim ersten Zugriff in die _instance Variable gespeichert wird.

Grüße

3.170 Beiträge seit 2006
vor 7 Jahren

Hallo,

also die Klasse aus dem Ursprungspost funktioniert (ich habe nur die Ausgabefunktion geändert, um die Typen zu sehen):

    class Base
    {
    }

    class Derived1 : Base
    {
    }

    class Derived2 : Base
    {
    }

    class Singleton<T> where T : Base
    {
        protected T entityType;

        protected static Singleton<T> instance;

        protected Singleton(T value)
        {
            entityType = value;
        }

        public static Singleton<T> Instance(T value)
        {
            if (instance == null)
            {
                Console.WriteLine("instance == null");
                instance = new Singleton<T>(value);
            }

            PrintValue();

            return instance;
        }

        static void PrintValue()
        {
            Console.WriteLine(typeof(T).FullName);
            Console.WriteLine(instance.entityType.GetType().FullName);
        }

    }

Wenn ich das ganze so benutze, erhalte ich die gewünschte Ausgabe

class Prog 
    {
            static void Main()
            {
                // Singleton<Derived1> für Aufruf und Derived1 als Parameter
                Singleton<Derived1>.Instance(new Derived1());

                // Singleton<Derived2> für Aufruf und Derived2 Parameter
                Singleton<Derived2>.Instance(new Derived2());

                Console.ReadLine();
            }
    }

liefert

instance == null
ConsoleApplication1.Derived1
ConsoleApplication1.Derived1
instance == null
ConsoleApplication1.Derived2
ConsoleApplication1.Derived2

Den beschriebenen Effekt kann ich aber auch erzeugen, indem ich die Klasse anders (und in dem Fall falsch) benutze:

            static void Main()
            {
                // Singleton<Base> für Aufruf und Derived1 als Parameter
                Singleton<Base>.Instance(new Derived1());

                // Singleton<Base> für Aufruf und Derived2 als Parameter
                Singleton<Base>.Instance(new Derived2());

                Console.ReadLine();
            }

Dann erhalte ich das beschriebene Verhalten in der Ausgabe:

instance == null
ConsoleApplication1.Base
ConsoleApplication1.Derived1
ConsoleApplication1.Base
ConsoleApplication1.Derived1

Ich vermute also, dass Du versuchst, die Typisierung nur durch den Parameter zu machen, die Singleton-Klasse selbst aber mit der Basisklasse benutzt. Dass das nicht funktionieren kann, ist aber logisch.

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

O
Ornithopter Themenstarter:in
9 Beiträge seit 2017
vor 7 Jahren

Den zeitgleich erfolgten Vorpost habe ich noch nicht berücksichtigt, tue das aber gleich. Vielleicht löst das mein Problem.

also ich konnte jetzt beim überfliegen nichts sehen, was dein Verhalten erzeugen könnte.
Zumal ja zur compile Zeit aus den Generischen Typen Konkrete Typen werden.

Ich Habs mal ausprobiert, und es hat so funktioniert wie erwartet.

Danke, ich hatte vermutet, dass ich die Funktionsweise von Generics falsch verstanden hatte. Ich suche nochmal nach anderen Fehlerquellen.

Obwohl ich es komisch finde das Instance als Methode mit einem Parameter implementiert ist, der nur beim ersten Zugriff in die _instance Variable gespeichert wird.

Das ist die Grundvariante von MSDN:
Implementing Singleton in C#

Wenn ich das Problem richtig verstehe, wollt ihr lediglich die Daten auf Serverseite durch euer Singleton cachen?
Falls ja, wäre das Generic Singleton nicht umbedingt der beste Ansatz.
Hier musst du es auch noch Thread Safe arbeiten, damit parallele Abfragen nicht zu X Ladeanfragen gegen die DB führen.
Im Umkehrschluss hast du in deinem Generic Singleton dann aber noch das Problem, wenn du nur ein Lock Objekt hast.
Dann locken sich wegen dem Generic Singleton auch alle anderen Singletons gegenseitig weg.
Dann steht deine CPU irgendwann auf 100% und deine Anwendung reagiert nicht mehr weil sich die Anfragen blockieren.

Hier stellt sich die Frage ob eine Cache Container Klasse, also ein Container für eure Klassen Objekte nicht besser wäre.
Ein DataTable als Cache zu verwenden produziert enormen Overhead und frisst auch viel mehr Speicher als nötig.

Im Projekt sollen zunächst vor allem DataGridViews zur Anzeige und Eingabe von Daten eingesetzt werden. Diese verwenden als DataSource DataTables. Welche DataTable gerade angezeigt wird, wird über eine ComboBox ausgewählt. Falls man zur Anzeige der Daten zwischen verschiedenen DataTables hin und her wechselt, soll nicht jedes Mal die DataTable neu aus der Datenbank geladen werden. Insbesondere, falls der Nutzer mit Pfeiltasten durch die ComboBox navigiert und zwischendurch noch andere DataTables aufgerufen werden.

Vorläufig genügt es völlig, dass nur eine Instanz der Anwendung gleichzeitig schreibenden Zugriff erhält. Das Projekt soll allerdings später auch als Basis für ein weiteres Projekt dienen, in dem das evtl nicht mehr reicht. Jedoch wäre es auch wichtig in absehbarer Zeit Ergebnisse zu präsentieren. Da ich mich in Threading und gleichzeitigen Datenbankzugriff erst einarbeiten müsste, würde ich das gerne erstmal außen vor lassen.

Ich würde hier eher pro Singleton eine eigene Klasse für das Singleton anlegen um die Typen auch wirklich getrennt zu haben.

Ich habe nun exemplarisch eine Klasse dafür angelegt:


    public class InventaryObjectTypeSingleton : Singleton<InventaryObjectType>
    {
        private InventaryObjectTypeSingleton(InventaryObjectType value) : base(value)
        {

        }
    }

Ich erhalte hier jedoch in der return-Zeile einen Konvertierungsfehler, der sich mir irgendwie nicht erklärt:


    public class InventaryObjectType : ObjectType
    {
        public override Singleton<DataBaseEntityType> SingletonDataBaseEntityType
        {
            get
            {
                return (Singleton<DataBaseEntityType>)InventaryObjectTypeSingleton.Instance(this);
            }
        }

Der Typ "InventaryManagementProject.DataBaseEntityTypes.Singleton<InventaryManagementProject.DataBaseEntityTypes.ObjectTypes.InventaryObjectType>" kann nicht in "InventaryManagementProject.DataBaseEntityTypes.Singleton<InventaryManagementProject.DataBaseEntityTypes.DataBaseEntityType>" konvertiert werden.

Also Singleton<InventaryObjectType> kann nicht in Singleton<DataBaseEntityType> konvertiert werden. Allerdings ist DataBaseEntityType die Basisklasse von ObjectType und das ist die Basisklasse von InventaryObjectType.

Instance liefert immer einen Singleton<DataBaseEntityType> und muss doch dann auch in einen solchen konvertiert werden können:


    public class Singleton<T> where T : DataBaseEntityType
    {
        public static Singleton<T> Instance(T value)

5.657 Beiträge seit 2006
vor 7 Jahren

Hi Ornithopter,

ich hatte vermutet, dass ich die Funktionsweise von Generics falsch verstanden hatte.

Hast du. Deine verkehrten Annahmen zum Konvertieren zwischen generischen Typen führen zu dem Compilererror. Dazu solltest du dich nochmal belesen.

Falls man zur Anzeige der Daten zwischen verschiedenen DataTables hin und her wechselt, soll nicht jedes Mal die DataTable neu aus der Datenbank geladen werden.

Ich verstehe nicht, inwieweit da eine Singleton-Implementierung dabei helfen sollte. Bzw. was der Vorteil gegenüber Instanzen-Variablen oder eine Liste/Dictionary mit den DataTables sein soll.

Das Projekt soll allerdings später auch als Basis für ein weiteres Projekt dienen, in dem das evtl nicht mehr reicht.

Dann würde ich empfehlen, auf DataTables zu verzichten und stattdessen mit einem vernünftigen Datenmodell zu arbeiten und evtl. einen ORM wie den Entity Framework zu verwenden.

Weeks of programming can save you hours of planning

O
Ornithopter Themenstarter:in
9 Beiträge seit 2017
vor 7 Jahren

Falls man zur Anzeige der Daten zwischen verschiedenen DataTables hin und her wechselt, soll nicht jedes Mal die DataTable neu aus der Datenbank geladen werden.

Ich verstehe nicht, inwieweit da eine Singleton-Implementierung dabei helfen sollte. Bzw. was der Vorteil gegenüber Instanzen-Variablen oder eine Liste/Dictionary mit den DataTables sein soll.

Das Programm umfasst unterschiedliche Teile. In jedem einzelnen Bereich eine Instanz-Variable zu verwenden würde wieder unnötiges Laden verursachen. Globale Variablen würden funktionieren, allerdings soll man die ja vermeiden.

Das Projekt soll allerdings später auch als Basis für ein weiteres Projekt dienen, in dem das evtl nicht mehr reicht.

Dann würde ich empfehlen, auf DataTables zu verzichten und stattdessen mit einem vernünftigen Datenmodell zu arbeiten und evtl. einen ORM wie den Entity Framework zu verwenden.

Das Problem ist, dass die letztlich vom Benutzer verwendeten Typen wie Arbeitsspeicher, Monitor, Headset, ... über eine vom Benutzer erst nach Fertigstellung des Programms festzulegende variable Anzahl beliebig definierbarer Eigenschaften mit beliebig definierbaren Wertemengen verfügen sollen.

Wenn es nach mir ginge, würde ich auch gerne diese Typen im Programm fest codieren und dann Entity Framework verwenden und bei zusätzlich benötigten Eigenschaften diese in den Klassen ändern.

Allerdings wird es keine Klassen für Arbeitsspeicher, Monitor, Headsets ... geben, sondern nur vom Benutzer angelegte Datenbanktabellen. Ob der Benutzer am Ende damit wirklich Monitore und Headsets verwaltet, oder stattdessen Tische und Stühle, oder zu verkaufende Produkte, ist dem Programm nicht bekannt (bzw. wird langfristig auf mehrere Kategorien hinauslaufen).

D
985 Beiträge seit 2014
vor 7 Jahren

Man macht sich weder überall Instanz-Variablen, noch Singletons, noch globale Variablen.

Man schafft sich einen Kontext der diese Instanzen hält. Diesen Kontext übergibt man allen die mit diesem Kontext arbeiten sollen (also im Moment alle).

Kommst du irgendwann in die Verlegenheit einen weiteren Kontext zu benötigen, dann kostet es dich ein müdes Lächeln, ohne wachsen dir graue Haare.

5.657 Beiträge seit 2006
vor 7 Jahren

Ich kann deine Schlußfolgerungen nicht so richtig nachvollziehen. Weil ein Programm aus mehreren Teilen besteht, benötigt man globale Variablen? Und weil die Anforderungen noch nicht festgelegt sind, kann man keinen ORM einsetzen?

Ich verstehe deine Anforderungen jetzt so, daß du einen Viewer bzw. eine Oberfläche für eine Datenbank entwickelst. Die Datenbank wird dabei vom Kunden vorgegeben und deren Schema auch regelmäßig vom Kunden geändert. Und dein Programm ist nur für die Anzeige und das Ändern der Werte in den jeweils vorhandenen Tabellen zuständig.

Erzähl uns mal was über deine bisherige Umsetzung, und beschreib mal, an welcher Stelle es dabei erforderlich wird, Daten erneut aus der Datenbank zu laden. Hier muß man ansetzen, und mit einer geeigneten Software-Architektur würde auch die Notwendigkeit von globalen Variablen (oder Singletons) entfallen.

Weeks of programming can save you hours of planning

O
Ornithopter Themenstarter:in
9 Beiträge seit 2017
vor 7 Jahren

Ich bedanke mich auf jeden Fall schon mal für alle Hinweise.

Vielleicht sind einige Dinge für euch selbstverständlich, dir mir unbekannt sind. Ich arbeite an meinem ersten großen Projekt (groß für eine einzelne Person ohne Berufserfahrung). Wie eine gut gestaltete Softwarearchitektur aussehen kann, habe ich bislang nur ausschnittsweise gesehen. Und teilweise lerne ich das gerade durch Erfahrung mit meinem Projekt.
An der Uni habe ich abgesehen von totalen Grundlagen leider eher Theorie vermittelt bekommen und wenig fortgeschrittene, praxisorientierte Kenntnisse.
Von daher bin ich auch sehr dankbar für Hinweise, über welche Themen ich mich noch informieren sollte. Wenn man sich nicht auskennt, weiß man leider auch nicht, welche Themen relevant sind. Und einfach alles lesen geht auch nicht, ich soll ja Ergebnisse liefern…

Was ein **Kontext **im genannten Sinne ist, kann ich nur erahnen. Ich bin bislang noch nicht in die Verlegenheit gekommen einen solchen zu benötigen und hätte ihn erst dann entwickelt, wenn er benötigt wird.
**ORM **habe ich bislang noch nie komplett selbstständig eingesetzt, auch wenn ich damit schon stellenweise gearbeitet habe (NHibernate). So wie ich es verstanden habe, kann man damit die Instanzen der eigenen Klassen in einer Datenbank speichern. Die Daten, die ich speichern möchte, liegen jedoch nicht in Instanzen von von mir erstellten Klassen vor, da ich zu den Realweltobjekten, um die es geht, keine Klassen erstellen kann.

Ich entwickle ein Inventardatenverwaltungsprogramm, bei dem für verschiedene Inventarobjekte deren Eigenschaften sowie eine Referenz auf Ort (Lager oder Mitarbeiter), Lieferant und Mitarbeiter (falls es im Büro eines Mitarbeiters eingesetzt wird). Welche Inventarobjekttypen genau zu verwalten sind, soll erst durch den Benutzer im Programm definiert und dann in der Datenbank hinterlegt werden. Darunter werden voraussichtlich so Dinge wie Monitore, Headsets und Arbeitsspeicher sein. Später sollen vermutlich auch zu verkaufende Waren verwaltet werden.

Eine Klasse Arbeitsspeicher mit Eigenschaften wie
RAM = { 2 GB, 4 GB, 8 GB, … }
Arbeitsspeichertyp = { DDR2, DDR3, … }
soll nicht von mir als Klasse definiert werden, wie ich es gerne hätte, sondern vom Benutzer.

Alle Arbeitsspeicherobjekte speichere ich in einer extra Datenbanktabelle, alle Monitorobjekte in einer weiteren Datenbanktabelle usw.

Wenn es nach mir ginge, würde ich einfach folgende Klassen erstellen:


public class Monitor : InventaryObjectType
public class Headset : InventaryObjectType
public class Arbeitsspeicher : InventaryObjectType

Dann könnte ich die Objekte dieser Klassen per Entity-Framework speichern. Nur gibt es diese Klassen leider nicht und wie soll ich sie dann speichern?

Ich verstehe deine Anforderungen jetzt so, daß du einen Viewer bzw. eine Oberfläche für eine Datenbank entwickelst. Die Datenbank wird dabei vom Kunden vorgegeben und deren Schema auch regelmäßig vom Kunden geändert. Und dein Programm ist nur für die Anzeige und das Ändern der Werte in den jeweils vorhandenen Tabellen zuständig.

Ob das regelmäßig geändert wird, weiß ich nicht, ich bezweifle derzeit eher die Notwendigkeit, warum man das nicht im Programmcode ändern kann (schließlich ist das Hinzufügen von gleichartigen Klassen nicht aufwändig, wenn man auf die Wartbarkeit des Programms Wert gelegt hat), allerdings fehlt es mir auch an Erfahrung.

Analog zum InventaryObjectType, den ich oben schon beschrieben habe, gibt es insgesamt bisher:


public class InventaryObjectType : ObjectType
public class LocationObjectType : ObjectType
public class StaffObjectType : ObjectType
public class SupplierObjectType : ObjectType

Zu jedem ObjectType gibt es eine PropertyType-Klasse, z.B. InventaryObjectTypePropertyType.

Zwei Beispiele, die der Benutzer dort definieren kann, sind:
RAM = { 2 GB, 4 GB, 8 GB, … }
Arbeitsspeichertyp = { DDR2, DDR3, … }

Anschließend könnte der InventaryObjectType Arbeitsspeicher vom Benutzer definiert werden, dem der Benutzer dann die zuvor von ihm definierten InventaryObjectTypePropertyTypes (nicht aber die PropertyTypes anderer ObjectTypes) zuweist.
Bei der Eingabe neu gelieferter Arbeitsspeicher könnten dann für die Spalte RAM per DataGridViewComboBoxColumn nur die zulässigen definierten Werte ausgewählt werden und somit fehlerhafte Eingaben wie „3 GB“ verhindert werden.

Hauptfunktion: Lieferung erfassen
Verschiedene Registerkarten mit jeweils einer ComboBox, über das ein bestimmter InventaryObjektType (Headset, Monitor, … ) ausgewählt wird, so dass man in einem DataGridView die Daten der gelieferten Gegenstände eingeben kann.
Der Gesamtpreis der Lieferung soll automatisch auf die einzelnen gelieferten Gegenstände umgelegt werden, so dass also nicht manuell eingegebene, sondern auch vom Programm berechnete Daten in der Datenbank gespeichert werden.
Die Lieferung selbst wird auch gespeichert und dabei z.B. der Lieferant aus den vorhandenen Lieferanten ausgewählt.

Hauptfunktion: Statusänderung
Z.B.:
Bei Entnahme von Objekten aus dem Lager muss im Programm der Ortswechsel eingegeben werden.
Sollte ein Objekt defekt sein, muss dies vermerkt werden.

Hauptfunktion: Retourenmanagement

Spätere Hauptfunktionen: Einkauf und Online-Verkauf von Waren inklusive automatische Erstellung von Rechnungen und Rechnungsdatenverwaltung

Erzähl uns mal was über deine bisherige Umsetzung, und beschreib mal, an welcher Stelle es dabei erforderlich wird, Daten erneut aus der Datenbank zu laden. Hier muß man ansetzen, und mit einer geeigneten Software-Architektur würde auch die Notwendigkeit von globalen Variablen (oder Singletons) entfallen.

Daten müssen dann aus der Datenbank geladen werden, wenn sie entweder noch nicht geladen wurden oder wenn sie geändert wurden. Vorläufig darf nur eine Instanz des Programms gleichzeitig schreibenden Zugriff haben, so dass das Programm dann selbst einen Refresh auslösen kann, der alle geänderten DataTables nach dem Übermitteln an die Datenbank auf null setzt, so dass das Programm sie bei der nächsten Verwendung neu laden muss.

Sollte das Programm so wie erwartet weiterentwickelt werden, könnte es aber auch notwendig werden, dass mehrere Instanzen des Programms gleichzeitig schreibenden Zugriff haben müssen. Falls ich in den nächsten Wochen nichts liefere, weiß ich aber nicht, ob ich dann noch derjenige bin, der dafür zuständig ist. Insofern möchte ich natürlich Dinge, die hinterher sonst sehr kompliziert werden, schon berücksichtigen, aber ansonsten auch nur das implementieren (und mich darein einarbeiten), was für eine vorzeigbare erste Version nötig ist.

Vieles ist für mich leider schwer abschätzbar, da ich in vielen Dingen leider noch keine konkrete Erfahrung gemacht habe, auf die ich zugreifen kann, um es einzuschätzen.

Ich hoffe, dass ich alle Fragen damit explizit oder implizit beantwortet habe, ansonsten gerne nochmal nachfragen, falls es sich noch nicht erledigt hat.

1.029 Beiträge seit 2010
vor 7 Jahren

Hi,

entschuldige bitte - aber die Funktionen die du erwähnst gehören offenkundig zu einem ERP-System. Du willst das allein machen? Ich hatte auch mal solche Pläne, bis ich den Aufwand dafür besser verstanden habe.

Da du offenkundig nicht wirklich drin bist - sag ich's dir: Lass die Finger davon und zieh die Notbremse. Sofern du kein Team hast, beabsichtigst das Ding an zig hundert Kunden zu verkaufen - wirst du kein ERP schreiben, dass es auch nur im Ansatz mit einem anderen ERP-System aufnehmen kann. Fertige Systeme sind bewusst flexible, verfügen über Modul/Plugin/AddIn-Systeme, erfüllen rechtliche Anforderungen, etc. pp.

Was auch immer du fabrizierst - wird im Endeffekt nicht das machen, was dann dein Kunde braucht und dennoch teurer sein als ein System, dass dies alles kann.

Die übliche Vorgehensweise für solche Fälle ist:
a) Konkrete Anforderungen erfassen
b) Programme suchen, die diesen Anforderungen gerecht werden können oder so erweitert werden können
c) Programm anpassen/anpassen lassen (Und an dieser Stelle kannst du dann vll programmieren)

Und nein - ich bin kein Profi-Entwickler, der hier eine Lanze für Profisoftware brechen möchte - eigentlich hatte ich mal ein gleichartiges Problem.

LG

849 Beiträge seit 2006
vor 7 Jahren

Das ist die Grundvariante von MSDN:
Implementing Singleton in C#

Nein, in allen im Artikel dargestellten Varianten ist Instance ein Property und keine Funktion. Die Idee des Singleton beruht ja gerade darauf, das Du die Instance halt nicht von jeder beliebigen Stelle Initialiersieren kannst, sondern das dieses beim ersten Aufruf Zentral und Kontrolliert passiert. In deiner Variante ist eher so ein "Der erste gewinnt" implementiert.

W
955 Beiträge seit 2010
vor 7 Jahren

... ich verstehe sowieso nicht warum er das nicht mit Lazy<T> und einem vernünftigen Dependency Injection Container macht ....

P
1.090 Beiträge seit 2011
vor 7 Jahren

Bei kleineren Datenmengen ist eine Datenbank meist so schnell das der Benutzer es gar nicht mit bekommt. Bei größeren Datenmengen verwendet man meist Pageing und Lade nur die Daten die der Benutzer braucht, was dann auch wieder schnell genug ist.

Wenn du alle Daten in den Speicher lädst kommst du z.B. bei 32bit Systemen schnell an die grenze. Und sobald mehrere Clients auf die Datenbank zugreifen muss du auch noch schauen wie du die Daten synchronisierst.

Grundlegend Cacht ja EF die Daten auch schon. Google mal zuzammen mit EF Repository Pattern und die Unit OF Work. Das sind eigentlich so die Standard Pattern die man für den Datenzugriff benutzt. Prisem kann man sich auch mal anschauen, du wirst ja noch ein bisschen mehr als nur Datenbank abfragen für dein Projekt brauchen.

Wenn es dein erstes Projekt ist schau dir doch erst mal in ruhe an, wie andere grundlegende Sachen umsetzten. z.B. vileicht findest du auch ein Open Source ERP-System was du dir Anschauen kannst. Schau vorallem erstmal, das du so viel wie möglich nach Standards Programmierst, da kannst du dann einfach googlen wenn du Probleme hast. Wenn du alles selbst "erfinden" will hast du schnell ein Eckiges Rad erfunden.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

16.806 Beiträge seit 2008
vor 7 Jahren

EF cached nicht per default. Das ist falsch.
Das EF cached nur die Find-Methode, alle anderen cachen nicht und muss über eigenen Code realisiert werden.

Für den DAL kann sich sehr schnell auch der CQRS Pattern lohnen, gerade, wenn man viele Entitäten hat und nicht immer alles laden muss, sondern eher viele Projektionen hat.

P
1.090 Beiträge seit 2011
vor 7 Jahren

Sorry hab mich da missverständlich ausgedrückt, ich meinte den first-level cache.

The Entity Framework (EF) ObjectContext and DbContext maintain state information about entities they’re managing. But once the context goes out of scope, that state information is gone. This type of caching is referred to as first-level caching and is only available for the lifetime of a transaction.

MSDN:Data Points - Second-Level Caching in the Entity Framework and AppFabric

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern