Idee zur sicheren(?) Globalisierung von Daten. Bitte im Meinung.
-
Mal wieder Grüße an das Forum!
Also mir kam grad sone Idee, wie man die Datenübergabe an verschiedene Forms und Subforms stark vereinfachen, bzw. ganz umgehen kann, und möchte Euch um Eure Meinung dazu bitten, da ich noch nicht so viel Erfahrung mit Entwicklung größerer Anwendungen habe.
Also: Statt in der Hauptform die Member zu deklarieren (Dictionaries, DataTable, usw) und an jede weitere Form die Referennz zu übergeben, usw., schreibe ich eine eigene Klasse mit ebendiesen Members als statische Variablen. Auf diese Members können dann alle Forms innerhalb des Namespaces direkt zugreifen. Macht das Programm um einiges leichter wartbar, kann ich mir erstmal soweit vorstellen.
Aber was hat das noch für (unangenehme) (Neben-)Wirkungen? Was haltet Ihr davon?
danke und mfg
tobi
-
Und das Bringt genau was? Ausser ggf einen unmengen an Speicherverbrauch weil du irgendwie alles auf vorrat hälst? Oder hab ich an der Idee jetzt was grundlegend falsch verstanden?
-
Das Datenmodell der Anwendung hat weder was in der Hauptform noch in einer statischen Klasse verloren.
-
Vorab: die Daten sind in sql gespeichert. Ich hab ne MDI-Anwendung. Die Daten sind jetzt so organisiert:
Ich habe eine Klasse für die Daten aus einer sql-Tabelle, von ihr die Values eines
Dictionary
, wobei die Keys die Ids für die Datensätze sind. Dieses Dictionary wird bei Programmstart mit den Daten aus sql befüllt. Es ist member der Hauptform, und wird zur Laufzeit je nach Aktion geändert mit gleichzeitigen Update-Anweisungen an die sql-DB. D.h. war, so hatte ich es vorher gelöst. Nun ist es statisches member dieser Klasse (s.u.) und auf es kann von allen Forms aus zugegriffen werden.Genauso benutze ich einen
DataTable
. Das sind jetzt 2 Beispiele von noch anderen Variablen, die zur gesamten Laufzeit des Programms ihre Gältigkeit haben.Diese Daten müssen im Arbeitsspeicher sein, weil ich nicht bei jedem Paintereignis eine sql-Verbindung herstellen kann. Und als statische Member einer Klasse, weil ich sie eben nur einmal brauche. (Oder habe ich da jetzt etwas vollkommen falsch verstanden??)
@LordJaxom , wo würdest denn du die dann z.B. unterbringen, wenn das "nichts in der Hauptform verloren hat"?
@Fedaykin , ich hoffe, es ist jetzt etwas verständlicher geworden, was ich versuche.
Aber ich bin natürlich für jede Kritik zu haben.
Denn rein intuitiv würde ich bisher auch sage, es ist eine ziemlich "wilde" Lösung. Aber strukturell,d.h. technisch sehe ich erstmal nur Vorteile.
(sry, zu schnell auf Absenden geklickt ;)...)
Ihr könnt Euch den code vielleicht auch vorstellen. Aber zu Veranschaulichung trotzdem: So siehts bisher aus, und sehr viel mehr wirds auch nicht werden, denn wo immer ich kann, werden die Daten local verwaltet. Nur diese brauche ich App-übergreifend:
namespace ZiVerwg { class AppDatas { public struct stBuchung { public DateTime Von; public DateTime Bis; public string ZiId;} public static Dictionary<string, CZimmer> ZiListe { get; set; } public static DataTable GKartei { get; set; } public static string sqlConStr = "Data Source= ... usw. das übliche"; public static stBuchung Buchung; } }
(ich grüble weiter, nachdem ich absende... schon 5. edit, aber bisher ja noch keine Anwort "überschrieben")
Wahrscheinlich liegt was Hauptproblem darin, die Daten sämtlich als public zu deklarieren. Das sind Bereiche (also damit verbindene Gefahren) mit denen ich eben leider noch gar keine Erfahrung habe...
edit 6: bin sehr gespannt, was noch an Kommentaren kommt!
-
Es ist denkbar unklug immer alle Daten im Speicher zu haben. Es reicht aus nur das bei dem du vermutest es wird gebraucht schon vorab etwas zu Cachen.
Schreib dir eine Art Datenmanager. Lade ein paar Datensätze die du im Hauptform brauchst auf verdacht. Wenn du ein Grid oder ähnliches hast kannst musst du ja nicht bei jedem Scrollen neue Datensätze laden. Wenn du dann eine Detailsicht öffnest lädst du die neuen Daten aus der Datenbank nach und gibst sie der Form mit. Die Form kann sich den Datensatz ja merken. Damit musst du auch nicht bei jeden Paint neu laden. Und wenn du erwarten kannst das du die Daten demnächst nochmal brauchst Cache sie irgendwo ab. (Daher wieder ein zentraler Datenmanager der den SQL zugriff regelt und ggf ein paar Wichtige Datenzugriffe abcached).
Wenn du schon ein SQL System verwendest dann ist zu erwarten das die Datenmenge so groß werden kann das sie eh nicht mehr in den Ram passt, es ist unsinn ein DB system zu verwenden und dann immer die gesamte DB im Ram zu haben. Also entweder hast du so wenig Daten das alles im Ram stehen kann, dann brauchst du auch kein SQL. Da reichen Dateien die du beim Start einließt. Oder du hast ein DB System, dann hats keinen Sinn alles irgendwo im Ram zu haben oder Statisch hin und her zu reichen.
-
Ok, guter Einwand. Ich verstehe in etwa, was Du meinst.
Ich habe vorhin dann auch überlegt, wie würde ich das programmiere, wenn ich nicht wie jetzt zu reinen Übungszwecken 100-200 Datensätze habe, sondern 20000 oder mehr. Ich merke, wieviel ich noch lernen muss. Das ganze Gebiet cache-Verwaltung z.B. ist mir auch noch völlig neu. Man kann ja mit VS verschiedene Datenbanken anlegen. Werd da wohl noch einiges an Dokumentationen durchwälzen müssen.
Jedenfalls Danke für Deine Hinweise.
mfg
-
Wenn es zur Übung sein soll. Vergiss erstmal Caching.
Überleg dir ein Interface zur Datenbeschaffung.IDataCollector { //Liefert alle buchungen für ein Zimmer in einen Zeitrahmen public IList<Buchung> getBuchung(DateTime von, DateTime bis, string zimmerid); //Liefert ein einzelnes Zimmer public Zimmer getZimmer(string zimmerId); //Lieffert mehre Zimmer zu den übergebenen Ids public IList<Zimmer> getZimmers(IEnumerable<string> zimmerIds); //Erzeugt eine neue Buchung im System public bool CreateBuchung(Buchung newEntry); //überleg was du noch mehr brauchen könntest }
Alternativ kannst du das ja immer noch weiter Aufsplitten. Vorteil ist halt das es egal ist woher wirklich die daten kommen du kannst einen IDataCollector implementierren der Daten aus einer DB liest, du kannst aber auch einen IDataCollector implementieren der dafür einen Webservice abfragt oder ähnliches.
Noch besser wäre es ggf sogar Buchung oder Zimmer als Interfaces zu definieren. Du lieferst dann im IDataConnector immer nur eine IBuchung oder IZimmer aber das ist ggf schon zu viel abstraktion. Man könnte jetzt sogar wenn Zimmer einen IDataConnector kennt direkt funktionalitäten ranimplementierenpublic abstract class DataObject { //hierüber holt sich dann das Datenobjekt ggf weitere Daten IDataConnector{ get; set; } } public class Zimmer { string id; bool hatFernseher; public Buchung Buche(DateTime von, DateTime bis) { if this.DataConnector.IstBuchungVorhanden(this, von, bis) { //Liefere Fehlerzustand oder ähnliches } else { //erzeuge Buchungsobjekt } }
Du solltest dir auch bei DB Systemen klar machen wann du daten in die DB scheiben möchstes. Viele Arbeiten so das die Daten direkt mit der Eingabe gespeichert sind. Du könntest natürlich auch erstmal ein Objekt (Buchung/Zimmer etc.) anlegen welches du durch einen Klick auf den Speichern Button in die Datenbank schreibst. Ggf müsste bei sofortiger speicherung der Werte, wenn der nutzer etwas ändert, der Datenzugriff etwas anders ablaufen. Da würde ich dann noch mit PropertyChanged events und ähnlichen arbeiten.
-
Ok, super. Vielen Dank erstmal für diese vielen guten Tips. Da ist natürlich noch viel viel Probieren und Testen für mich mit verbunden, bis ich sowas ausprobieren kann.
Aber den Ansatz zu kennen, ist erstmal das wichtigste.
Ich habe vorhin dann schonmal versucht, eine Datenquelle mit Linq zu SQL zu implementieren. Ich sehe definitiv die Vorteile, aber auch mein Wissensdefizit, z.B. auch, was die Unterschiede der DBs im Projekt ausmachen, usw.
Wir haben im Kurs bei den kleinen Übungen eben immer nur die Daten aus der DB local in der Form gespeichert... Viel Arbeit liegt noch vor mir...
Mein einziger bisheriger Schritt in die Richtung: Die Klasse für die Zimmer hat eine statische Methode, die den Zimmerstatus (von, bis) abfragt, aber muss natürlich auch nicht statisch sein, und mit Interfaces zu arbeiten, ist natürlich auch eleganter.
-
Die Daten welche die Form anzeigt lokal in dieser zu halten ist ja auch nicht verkehrt. Es sollte der Form nur egal sein woher sie kommen, und die Form sollte sie nur solange festhalten wie sie diese Daten echt braucht.
Bei größeren Projekten wird ja genau so eine Arbeitsteilung erzielt.
Man überlegt sich was man eigentlich für Daten braucht und definiert dafür einheitliche Interfaces.bei dir könnte das sowas sein
public interface IRoom { public string Id{ get; } public int Groesse { get; } public int RaumNummer { get; } public bool HatFernseher { get; set; } . . }
nun ist der Trick bei Großprojekten dabei das nur das Interface klar sein muss. Du könntest wenn du dies kennst ohne Probleme eine Eingabemaske für Räume programmieren. Dich braucht nicht zu interessieren woher die Daten kommen, was pasiert wenn du für einen IRoom was änderst. Du stellst seine Daten da, und änderst ggf intern seine Daten ab, ob nun sofort dabei Daten in die Datenbank geschrieben werden oder jemand das geänderte IRoom objekt von dir entgegen nimmt und dann erst in die Datenbank die änderung schreibt sind Implementionsdetails für denjenigen der IRoom implementiert.
Dadurch entkoppelt man sehr vieles. Und als Programmierer von gewissen Einzelheiten muss man nicht mehr auf so viele Randbedingungen achten. Leider ist das nur die Theorie, in der Praxis sieht das dann meist noch ganz anders aus. Man sollte zumindest selber versuchen bei eine gewisse Denkweise in die Richtung anzugewöhnen. Und wenns nur in der Theorie ist. Einfach bei Privatprojekten überlegen wie könnte man was in Interfaces so deklarieren das jemand anderes den ich überhaupt nicht kenne damit schon funktionalitäten Programmieren könnte.
-
Ich kann mir auch vorstellen, dass sich die Interfaces, jedenfalls bei laufenden Projekten, ständig erweitern, oder? Ich schreibe meine Klasse, und je nach Umfang der Vorplanung kommen weitere Methoden während der Entwicklung dazu. Wenn ich glaube, es ist soweit fertig, kann ich mir das Interface ja automatisch generieren lassen für andere Klassen. Bzw. zum Weitergeben oder wenns ne dll wird. Aber meistens bleibt es dann glaube ich nicht dabei, oder?
Und dazu ne andere Frage: Das lohnt sich ja doch eigentlich nur, wenn die andere Klasse die selben features hat. Meistens sind es dann aber doch andere. Also wofür dann noch ein Interface? Oder sind interfaces hauptsächlich für andere eine Orientierung, die mit meiner Klasse weiterarbeiten, bzw. umgekehrt?
Z.B. wenn wie du schreibst, sagen wir mal, mein Kollege eine IRoom-Eingabemaske schreibt?Zu der Datenbankverbindung: ich hab die Verbindungsobjekte und die sql-Anweisungen jetzt innerhalb der Methoden der Klassen, die die Daten brauchen. Oder würdest du das weiter auslagern? Wenn wenn ich jetzt ne andere Verbindung aufbauen will, evtl. mit anderer Syntax, dann muss die Klasse auch umgeschrieben werden.
Ich hab mal versucht, ein DataSet-Objekt über den Designer anzulegen: sql-Tabellen reingezogen, Verknüpfungen hergestellt, "Testansicht" der Daten im Designer funktionierte... Dann sollte das ausgelagert sein. Die Verbindung und alle Abfragen/Aktualisierungen/usw. sollten dann ja über das DataObjekt erledigt werden, dann sollte ich unabhängig davon sein. Aber 1. konnte ich dann zu Laufzeit nicht auf die Tabellendaten zugreifen (hab wohl noch was übersehen), 2. kommt mir das unheimlich starr und aufgebläht vor. Deshalb hab ich jetzt doch das Datenmanagement in eigenen Klassen verpackt, mit verschiedenen Ableitungen (z.b. von DataTable) (und ja, Instanzen davon werden im Haupt/Parentformular angelegt). Dann müsste ich später aber eben doch wieder in alle Klassen "einbrechen", wenns ein anderer Verbindungstyp wird.
-
Hallo TobiBob,
größere Projekte bestehen immer aus mehreren Schichten (z.B. 3-Tier: GUI, Business Classes (BC), Data Layer (DAL)).
Zur Entkopplung werden dann zwingend Interfaces eingesetzt, damit man niemals direkte Abhängigkeiten zu konkreten Implementierungen hat. Gerade zur Testbarkeit (insbs. der Business Classes) hat sich InversionOfControl (IoC) z.B. mittels Dependency-Injection (DI), etabliert.Deine Datanbankklassen sollten daher niemals direkt von der GUI instanziert (und benutzt werden), sondern nur von den Business Classes. Und gerade für .NET Anwendungen sollte man dann nur DataBinding (aus Sicht der GUI) benutzen!
Um die DB-Anbindung zu vereinfachen, werden dann meistens OR-Mapper benutzt (z.B. Linq-to-SQL, Entity Framework (EF), NHibernate, ...). Für kleinere Anwendungen gibt es dann auch Micro-ORMs, wie PetaPoco, Dapper.NET, Massive, ...Natürlich wird man bei privaten Projekten nicht sofort die gesamte Anwendung umbauen, aber je mehr Projekte man entwickelt, desto eher bekommt man einen Sinn für die Entkopplung der verschiedenen Teile der Software.
Wenn du z.B. das Datenbanksystem austauschen willst, dann sollten möglichst auch nur Änderung an dem Data Layer vorgenommen werden (die Business Classes und erst echt die GUI sollten davon unangetastet bleiben).
Und ähnlich sieht es natürlich auch auf Seiten der GUI aus - auch dort sollte ein Wechsel (im optimalen Falleauch keine Änderungen der BC oder des DAL nach sich ziehen.
Es ist sehr gut, daß du dir diese Gedanken zur Software-Gestaltung machst, denn Selbstreflektion und Abstraktionsvermögen sind notwendige Eigenschaften eines (guten) Software-Entwicklers.
P.S: Anstatt "Business Classes" spricht man auch häufig einfach von "Modell" oder "Logik".
-
Wow!!... Ja, da wird mir langsam so einiges klarer - noch auf seeehr abstrakter Ebene. Das erschlägt mich natürlich erstmal etwas
aber es ist sehr interessant. Vor allem: solche Dinge erfährt man in keinen Tutorien oder Dokumentationen; das ist rein aus der Praxis, soweit ich das beurteilen kann. Deshalb bin ich euch beiden (@Fedaykin) für Eure Antworten sehr dankbar! Es gibt zu denken! Aber im positiven Sinne. Und ich merke, auf noch was für einem Beginner-Niveau das Ding ist, an dem ich gerade baue! Aber ich habe ja Zeit...
-
TobiBob schrieb:
Vor allem: solche Dinge erfährt man in keinen Tutorien oder Dokumentationen; das ist rein aus der Praxis, soweit ich das beurteilen kann.
Natürlich gibt es sowas auch nachzulesen. Nennt sich "Fachbücher".
-
loks schrieb:
Nennt sich "Fachbücher".
Jaa, ist schon klar. Aber man kann immer nur in kleinen Schritten weiter gehen, wenn man noch am Anfang ist und in alle Richtungen gehen will. Sicher gibts es massenweise Fachbücher und -Zeitschriften, aber ich bin (noch) zu praktisch veranlagt. Die Notwenidigkeit für die Theorie muss ja (jedenfalls für mich) auch aus der Praxis kommen. Wo soll man sonst anfangen?