Access Datenbanken zur Laufzeit erstellen

  • VB.NET

Es gibt 29 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Access Datenbanken zur Laufzeit erstellen

    Hallo liebe Freunde!

    Ich habe den ganzen Tag gegoogelt und Foren durchstöbert. Jedoch bin ich nirgends auf das gestoßen was ich suche.
    Überall wird nur beschrieben wie man Daten in VORHANDENE Datenbanken schreibt, diese modifziert etc.

    Ich muss jedoch mit meiner Anwendung mehrere Access-Datenbanken zur Laufzeit erstellen. Beispielsweise (ist nur eine der Datenbanken die erstellt werden sollen) möchte ich Benutzerdaten in eine Access-Datenbank schreiben. Die Datenstruktur sieht (zur Zeit) so aus:

    VB.NET-Quellcode

    1. Structure BedienerStructure
    2. Dim ID_Nummer As Integer
    3. Dim Name As String
    4. Dim BildschirmName As String
    5. Dim AnmeldeCode1 As String
    6. Dim AnmeldeCode2 As String
    7. Dim AnmeldeCode3 As String
    8. Dim BenutzerBild As String 'serialisiertes Bild
    9. Dim Finger1 As String 'serialisierte biometrische Information
    10. Dim Finger2 As String 'serialisierte biometrische Information
    11. Dim Finger3 As String 'serialisierte biometrische Information
    12. Dim ManagerRecht As Boolean
    13. Dim Stornorecht As Boolean
    14. Dim AbschlussRecht As Boolean
    15. Dim UmsatzanzeigeRecht As Boolean
    16. Dim ArbeitsZeitAngemeldet As Boolean
    17. Dim gesperrt As Boolean
    18. End Structure
    19. 'MaxBedienerAnzahlEver = 100
    20. Public Bediener(MaxBedienerAnzahlEver) As BedienerStructure


    Diese Datenstruktrur wird sich bis zur Fertigstellung der gesamten Anwendung noch einige male Ändern (es werden zusätzliche Datenfelder hinzukommen).

    a) Meine erste Aufgabe ist es per Quellcode eine neue Access-Datenbank zu erstellen und dort entsprechende Spalten mit "Name", "BildschirmName", "Anmeldecode1" etc. einzufügen. Vielleicht hat jemand von Euch ein funktionierendes Codeschnippsel ?

    b) Idealerweise würde ich auch gleich bei der Erstellung der Datenbank-Tabellenspalten die Infos aus der Structure "BedienerStructure" nehmen. Somit wird die neue Datenbank automatisch mit den benötigten Tabellenspalten erstellt.

    An a) hapert es noch. Wenn ich weis wie a) funktioniert, dann ist b aber sicher auch kein Problem mehr.

    Zur Info: Mir ist klar, dass diese 100 Datensätze umfassenden Daten auch anders als in einer Datenbank gespeichert werden könnten. Ich habe diesen Datensatz nur als Beispiel hergenommen. Für die anderen Daten in der Applikation ist die Ablage in eine Datenbank jedoch wichtig.

    LG Roland
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „dive26“ ()

    Eine Access-Datenbank erstellen geht im Prinzip nicht (ohne Access). Aber du kannst ne komplett leere Access Datei in die Ressourcen packen und bei Bedarf so oft du willst rauskopieren.
    Danach dann Verbindung via Jet, ODBC oder wasauchimmer. Tabellen etc anlegen geht SQL ähnlich mit CREATE TABLE usw (AFAIK!) - einfach einlesen, irgendwo wirds ja stehen ("creating access tables programmatically")
    There is no CLOUD - just other people's computers

    Q: Why do JAVA developers wear glasses?
    A: Because they can't C#

    Daily prayer:
    "Dear Lord, grand me the strength not to kill any stupid people today and please grant me the ability to punch them in the face over standard TCP/IP."
    Ich habe die Lösung (bzw. meinen Fehler) gefunden. Ich hatte diesen Beispielcode bereits einmal integriert und getestet. Jedoch habe ich als Tabellennamen "User" gewählt und erhielt in der Zeile "cmd.ExecuteNonQuery()" jedesmal eine Fehlermeldung, dass der SQL Syntax nicht korrekt ist. Jetzt funktioniert das mit dem Erstellen einer neuen Datenbank schon mal.

    VB.NET-Quellcode

    1. Public Sub ErstelleBenutzerDatenbank()
    2. 'Hier wird die Datenbank mit den Benutzern erstellt
    3. 'Am Besten direkt aus der Auflistung bediener(x).x
    4. Dim sDatabasePath As String = "c:/datenbank.mdb"
    5. 'Routine verlassen, falls die Datenbank bereits existiert
    6. Dim fi As New FileInfo(sDatabasePath)
    7. If fi.Exists Then Exit Sub
    8. Dim catalog As Object
    9. catalog = CreateObject("ADOX.Catalog")
    10. 'catalog.create("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & sDatabasePath & "; Jet OLEDB:Database Password=" & MyDbPassword & ";")
    11. catalog.create("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & sDatabasePath)
    12. 'Dim conn As OleDbConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & sDatabasePath & "; Jet OLEDB:Database Password=" & MyDbPassword & ";")
    13. Dim conn As OleDbConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & sDatabasePath)
    14. conn.Open()
    15. Dim sql As String = "CREATE TABLE Bediener(" & _
    16. "ID COUNTER NOT NULL CONSTRAINT PK_ID_no PRIMARY KEY, " & _
    17. "Bearbeiten YesNo, " & _
    18. "Titel Text(200), " & _
    19. "Ordner Text(200), " & _
    20. "Länge Text(200), " & _
    21. "Erscheinungsjahr Text(200)" & _
    22. ")"
    23. Dim cmd As OleDbCommand = New OleDbCommand(sql, conn)
    24. cmd.ExecuteNonQuery()
    25. conn.Close()
    26. catalog = Nothing
    27. 'Process.Start(sDatabasePath) 'nur testweise
    28. End Sub


    Jetzt möchte ich nur noch in einer Schleife alle Elemente (z.B. "BildschirmName") und Datentypen (z.B. "String") der Struktur auslesen. Also nicht den Dateninhalt sondern die Strukturdefinition. Leider weis ich nicht wie ich an diese Daten progarmmtechnisch rankomme.

    VB.NET-Quellcode

    1. Structure BedienerStructure
    2. Dim ID As Integer
    3. Dim Name As String
    4. Dim BildschirmName As String
    5. Dim AnmeldeCode1 As String
    6. Dim AnmeldeCode2 As String
    7. Dim AnmeldeCode3 As String
    8. Dim BenutzerBild As String 'serialisiertes Bild
    9. Dim Finger1 As String 'serialisierte biometrische Daten
    10. Dim Finger2 As String 'serialisierte biometrische Daten
    11. Dim Finger3 As String 'serialisierte biometrische Daten
    12. Dim ManagerRecht As Boolean
    13. Dim Stornorecht As Boolean
    14. Dim AbschlussRecht As Boolean
    15. Dim UmsatzanzeigeRecht As Boolean
    16. Dim ArbeitsZeitAngemeldet As Boolean
    17. Dim gesperrt As Boolean
    18. End Structure
    19. Public Bediener(MaxBedienerAnzahlEver) As BedienerStructure
    20. for i as Integer = 0 to BedienerStructure.count-1
    21. 'Ausgabe der Klartextnamen (z.B. "Bildschirmname") und des Datentypes (z.B. String)
    22. Next i
    23. 'oder
    24. For each element in Bedienerstructure
    25. 'Ausgabe der Klartextnamen (z.B. "Bildschirmname") und des Datentypes (z.B. String)
    26. Next
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „dive26“ ()

    Ähnliche Aufgabenstellungen hatte ich kürzlich ebenfalls. Ich habe jahrelang mit ADO (ohne .NET) erfolgreich Datenbankanwendungen programmiert und mir schmeckt dieses aufgeblasene DataSet nicht und mit dem Entity-Framework bin ich auch nicht lieb Freund - unter anderem wegen so etwas, wie du es vorhin in einem anderen Thread angemerkt hast: Software lebt und muss ggf. nachträglich angepasst werden, Änderungen an der Datenstruktur ziehen in ADO.NET da grundsätzlich einen Wust an Aufwand nach sich, der in keinerlei Verhältnis zum Nutzen steht, wenn man "nur mal eben" eine weitere Spalte in eine Datenbanktabelle einfügen will.

    Nichtsdestotrotz gefiel mir die Vorstellung, streng typisierte Datenklassen zu haben (vor allem da ich ein Verfechter von "Option Strict On" bin), und so wurde aus der Not heraus eine kleine an meine Bedürfnisse angepasste Datenbank-Bibliothek geboren, die die Einfachheit von ADO mit der strengen Typisierung der automatisch generierten Datenklassen aus ADO.NET vereint.

    Ich kann (und will) hier aber nicht den kompletten Code rausgeben, aber ein paar Ansätze kann ich verraten:

    Eigene Attribute definieren Felder und Properties einer Klasse als "Datenbankfeld". Zusätzliche Eigenschaften so eines Attribut können dann festhalten, ob das Feld Teil eines Primärschlüssels ist, auf welchen DB-Typ es ggf. abweichend gemappt werden soll, ob die Spalte in der DB einen anderen Namen (Alias) hat, etc. Damit repräsentiert diese Klasse mit ihren derart ausgezeichneten Membern einen einzelnen Datensatz einer Datenbank Tabelle. Wird der Tabelle mal eine Spalte hinzugefügt, stört das die Klasse nicht, andererseits kann ich durch Einfügen einer einzigen Codezeile diese Änderung im Programm nachziehen.

    Eine "Database"-Klasse wird im Konstruktor mit dem Namen des .NET-Providers (z.B. "System.Data.OleDb") und dem ConnectionString gefüttert und bietet Methoden a la "Execute(Of T)", "Update()" oder "CreateTable(Of T)", aber auch Transaktionen u.ä., wobei T dann für die jeweilige Klasse steht, deren Felder mit dem DatabaseFieldAttribute() ausgezeichnet sind.

    Im Execute(Of T) erzeuge ich dann ein RecordSet(Of T), das wiederum eine Liste aller abgefragten Datensätze (= Typ T) enthält und auch neue (AddNew) aufnehmen oder aus der man bestehende löschen kann. Bei Update() oder Delete() Operationen wird jeweils mit dem für die abgefragte Tabelle bereitgehaltenen DataAdapter die Aktion jeweils mit der Datenbank synchronisiert. Ich hab sogar einen (mind. in Access funktionierenden - SQL Server Tests stehen noch aus) Mechanismus eingebaut, der Autowert-Felder (Autoincrement oder Sequences o.ä.) bei einem Insert() automatisch in das Objekt übernimmt.

    Die Verknüpfung des (intern und für den Benutzer transparenten) DataTable-Objekts mit der Liste von T-Objekten findet per Reflection statt, d.h. es werden einfach alle Member der Klasse T gesucht, die ein DatabaseFieldAttribute haben und den Attribut-Informationen entsprechend aus der DataTable ins T-Objekt (oder umgekehrt) geschrieben. Hierfür kann ich einen kleinen Codeschnipsel präsentieren (allerdings in C#):
    Spoiler anzeigen

    C-Quellcode

    1. private T CreateRecordFromDataRow(DataRow row)
    2. {
    3. T newRecord = new T();
    4. foreach (DatabaseField column in _columns)
    5. {
    6. FieldInfo fieldinfo = typeof(T).GetField(column.FieldName);
    7. if (fieldinfo != null)
    8. {
    9. try
    10. {
    11. if (Table.Columns.Contains(column.FieldDbName))
    12. {
    13. object dbValue = row.IsNull(column.FieldDbName) ? column.FieldDefault : row[column.FieldDbName];
    14. if (dbValue == null || fieldinfo.FieldType.Equals(dbValue.GetType()))
    15. {
    16. fieldinfo.SetValue(newRecord, dbValue);
    17. }
    18. else
    19. {
    20. fieldinfo.SetValue(newRecord, System.Convert.ChangeType(dbValue, fieldinfo.FieldType));
    21. }
    22. }
    23. else
    24. {
    25. string msg = string.Format("Das erwartete Feld {0} ist in der Datensatzliste nicht vorhanden.", column.FieldDbName);
    26. throw new InvalidFieldMappingException(msg, null);
    27. }
    28. }
    29. catch (Exception ex)
    30. {
    31. string msg = string.Format("Fehler beim Setzen des Defaultwerts in Feld {0} in einem neu erzeugten Datensatz-Objekt", column.FieldName);
    32. throw new InvalidFieldMappingException(msg, ex);
    33. }
    34. }
    35. }
    36. _records.Add(newRecord, row);
    37. return newRecord;
    38. }


    Das oben genannte "CreateTable(Of T)" liest ebenfalls über Reflection die beteiligten Member und ihre Attribute aus und erzeugt daraus jeweils das passende CREATE TABLE Statement. Auch das dürfte von besonderem Interesse sein:
    Spoiler anzeigen

    C-Quellcode

    1. private bool CreateNonMappedTable(String Tablename, Type recordType)
    2. {
    3. List<FieldMapping> fieldMappings = new List<FieldMapping>();
    4. foreach (FieldInfo fi in recordType.GetFields())
    5. {
    6. FieldMapping map = new FieldMapping();
    7. object[] fieldAttribs = fi.GetCustomAttributes(false);
    8. foreach (Attribute attrib in fieldAttribs)
    9. {
    10. DatabaseFieldAttribute DbFieldAttrib = attrib as DatabaseFieldAttribute;
    11. if (DbFieldAttrib != null)
    12. {
    13. map.Fieldname = (DbFieldAttrib.Alias.Length > 0) ? DbFieldAttrib.Alias : fi.Name;
    14. map.DefaultValue = (DbFieldAttrib.DefaultValue == null) ? GetDefault(fi.FieldType) : DbFieldAttrib.DefaultValue;
    15. map.Datatype = GetSqlTypename(fi.FieldType, DbFieldAttrib.Length);
    16. map.AutoIncrement = DbFieldAttrib.AutoIncrement;
    17. if (!string.IsNullOrEmpty(map.Datatype))
    18. {
    19. fieldMappings.Add(map);
    20. }
    21. else
    22. {
    23. string msg = string.Format("Das Feld {0} Ihrer Datenklasse hat keinen korrespondierenden Datentyp in der gewählten Datenbank.", fi.Name);
    24. throw new InvalidDataTypeException(msg, null);
    25. }
    26. }
    27. else
    28. {
    29. PrimaryKeyAttribute DbKeyAttrib = attrib as PrimaryKeyAttribute;
    30. if (DbKeyAttrib != null)
    31. {
    32. map.PrimaryKeyIndex = DbKeyAttrib.Index;
    33. }
    34. }
    35. }
    36. }
    37. List<String> FieldDefs = new List<String>();
    38. foreach (var map in fieldMappings)
    39. {
    40. string def = string.Format("{0}{2}{1} {3}", QuotePrefix, QuoteSuffix, map.Fieldname, map.Datatype);
    41. if (map.AutoIncrement)
    42. {
    43. // TODO: Prüfen, ob die Verzweigung bei SQL-Server, Oracle und anderen Datenbank-anbietern korrekt funktioniert
    44. if (!_reservedNames.Contains("AUTO_INCREMENT"))
    45. //if (_isJet)
    46. {
    47. def += " IDENTITY (1,1)";
    48. }
    49. else
    50. {
    51. def += " NOT NULL AUTO_INCREMENT";
    52. }
    53. }
    54. FieldDefs.Add(def);
    55. }
    56. fieldMappings.Sort(delegate(FieldMapping x, FieldMapping y) { int diff = x.PrimaryKeyIndex - y.PrimaryKeyIndex; return diff; });
    57. List<String> PrimaryKeys = new List<String>();
    58. foreach (var map in fieldMappings)
    59. {
    60. if (map.PrimaryKeyIndex >= 0)
    61. {
    62. string key = string.Format("{0}{2}{1}", QuotePrefix, QuoteSuffix, map.Fieldname);
    63. PrimaryKeys.Add(key);
    64. }
    65. }
    66. if (FieldDefs.Count > 0)
    67. {
    68. if (PrimaryKeys.Count > 0)
    69. {
    70. string primkey = string.Format(" PRIMARY KEY ({0})", string.Join(",", PrimaryKeys.ToArray()));
    71. FieldDefs.Add(primkey);
    72. }
    73. string fieldlist = string.Join(", ", FieldDefs.ToArray());
    74. string sql = string.Format("CREATE TABLE {0}{2}{1} ({3})", QuotePrefix, QuoteSuffix, Tablename, fieldlist);
    75. using (DbConnection conn = GetNewConnection())
    76. {
    77. try
    78. {
    79. ExecuteNonQuery(conn, sql);
    80. return true;
    81. }
    82. catch (Exception ex)
    83. {
    84. string msg = string.Format("Beim Anlegen der Tabelle mit dem Basisnamen {0} ist ein Fehler aufgetreten.", Tablename);
    85. throw new CantCreateTableException(msg, ex);
    86. }
    87. }
    88. }
    89. throw new TargetParameterCountException();
    90. }


    Wichtig war mir z.B. auch die Unabhängigkeit vom .NET Provider. D.h. es ist völlig egal, ob der "Benutzer" meiner Bibliothek OleDb, Odbc, SqlServer oder sonstwas benutzen will, mit Hilfe des Provider-Namespace wird einfach die passende DbFactory erzeugt, die sich fortan um die Erzeugung jeglicher DbXxx-Objekte kümmert und notwendige Metadaten liefert, wie z.B. QuotePrefix, QuoteSuffix und solche Sachen.

    PS: Viel von meiner Arbeit basiert auf Informationen die ich (unter anderem) aus folgenden Artikeln gewonnen habe:
    Provider independent code simplified in ADO.NET 2.0
    Inserting relational data using DataSet and DataAdapter
    ADO+.NET
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    Danke Arby!

    Ich habe mittlerweile meine Lösung gefunden :thumbup: :thumbup:

    VB.NET-Quellcode

    1. 'Test ob man einzelne werte einer Struktur auslesen kann
    2. Dim TestString As String = ""
    3. Dim TempBedStruct As New BedienerStructure
    4. Dim TempWert As String
    5. For Each fi As System.Reflection.FieldInfo In TempBedStruct.GetType().GetFields
    6. TempWert = fi.GetValue(TempBedStruct)
    7. TestString += fi.Name + "(" + CStr(TempWert) + ")" + vbCrLf
    8. Next
    9. MsgBox(TestString)


    Das listet mir die Namen auf und in den Klammern die Werte. Jedoch kann ich da noch nicht unterscheiden ob Integer, Double etc. (siehe Screenshot).

    Daher würde ich gerne wissen wie ich hiervon den exakten Datentyp rauslesen kann.
    Bilder
    • 29102013180644.jpg

      59,36 kB, 223×379, 235 mal angesehen
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    Arby schrieb:

    unter anderem wegen so etwas, wie du es vorhin in einem anderen Thread angemerkt hast
    ich nehme an, du meinst das hier?
    Darauf hab ich nicht geantwortet, weils ein uralt-Thread ist. Aber inhaltlich wollte der TE dort zur Laufzeit Tabellen verändern, und davon halte ich nix.
    Dass sich ein Datenmodell gelegentlich ändert ist numal nicht zu ändern. Und das sind dann glaub immer mittlere oder auch schwere Eingriffe in die Programm-Struktur und in die Datenbank. Sowas macht man nicht zur Laufzeit, sondern das stellt ein UpDate dar, mit Änderungen sowohl am Server als auch an den Clients.

    Änderungen an der Datenstruktur ziehen in ADO.NET da grundsätzlich einen Wust an Aufwand nach sich, der in keinerlei Verhältnis zum Nutzen steht.
    Hmm - ja, also das Gedöhns mit den generierten TableAdaptern ist mir auch gewaltig auf den Keks gegangen. Das hat mich dazu gebracht, ein klein Framework zu schreiben, was sich die DataAdapter selbst konfiguriert - das ist nämlich dank der CommandBuilder-Klasse gar nicht soo schwierig, vom Prinzip her: DBExtensions.
    Mit diesen Extensions muß man halt die Strategie befolgen, das Dataset immer kompatibel zur DB zu halten - nichts weiter. Also kann man eine DB-Spalte hinzufügen - da passiert noch garnix im Dataset, solange diese Spalte Nullwerte akzeptiert.
    Üblicherweise soll aber was damit passieren, und dafür muß man halt die Komplement-Spalte auch im Dataset anlegen - ist ja nix anneres, als wenn du deine Datenklassen anpasst, damit du damit gegen die neue DB-Struktur proggen kannst.
    Jo, und da denke ich, beim Dataset ist der Wartungs-Aufwand geringer, weil die Vorgehensweise ganz stereotyp ist, und weil der Dataset-Designer nicht vergesslich ist, und keine Flüchtigkeitsfehler macht, und immer gleich alle viere komplett abhandelt: Select, Insert, Update, Delete.
    Sowas macht man nicht zur Laufzeit, sondern das stellt ein UpDate dar, mit Änderungen sowohl am Server als auch an den Clients.
    Dem stimme ich nicht zu bzw. trifft das auf meine Anwendung nicht zu.

    Es ist doch viel einfacher, wenn der User nur die .exe Datei per Update erneutern muss (automatisiert) und dann beim ersten Start die neue Tabelle automatisiert eingefügt wird. Der unbedarfte Benutzer wird von diesem technisch notwendigen Krimskrams nicht belästigt und kann mit seiner Software wie gewohnt weiterarbeiten, profitiert aber von den neu implementierten Funktionen (bzw. neuer notwendiger Aufzeichnungen, weil das Finanzministerium jedes Monat andere Daten gespeichert haben möchte).

    Du musst wissen, dass es bei meiner Anwendung keine Server/Client Architektur gibt (darauf verzichte ich aus gutem Grund), sondern nur lokale Installationen. Wenn zwei oder mehrere Kassen im Netzwerkverbund betrieben werden, dann "spielt" eine den Server mit Datenfreigabe auf den Ordner per Netzwerkpfad. Keep it simple. Meine Software soll ein Laie installieren können ohne dazu ein Datenbankingenieur sein zu müssen. Installieren und losarbeiten.

    Meinen Weg zum Erstellen der Datenbanken habe ich nun gefunden - das funktioniert soweit auch. Danke für Eure Hilfe.
    Das mit dem Ermitteln der Datentypen der Struktur wäre schön zu wissen, ist aber nicht essentiell um meine Anwendung voranzubringen.

    LG Roland
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    ErfinderDesRades schrieb:

    Aber inhaltlich wollte der TE dort zur Laufzeit Tabellen verändern, und davon halte ich nix.

    Da unterscheiden wir uns. Wir haben ebenfalls ein Programm in unserem - ich nenne es mal - "Framework", das auf einer Access-DB basiert und alle paar Monate kommt ein Kunde und sagt: "Ich hätte gern..."
    Dann wird das - nach entsprechendem Auftrag - gemacht und er bekommt die neue Exe direkt zugeschickt. Und wenn es eine Änderung ist, die den "Standard" - also auch die anderen Kunden - betrifft, dann wird das nicht per Rollout verbreitet, sondern erst im Rahmen des nächsten Updates. In der Zwischenzeit kann aber ein anderer Kunde einen bis dato unbekannten Fehler gefunden haben, weshalb er ebenfalls eine aktuelle Version braucht. Also wird der Fehler - so er noch vorhanden ist - behoben und er kriegt ebenfalls die neue Exe. Wenn nun kein Mechanismus in dem Programm dafür sorgen würde, dass in der Zwischenzeit vorgenommene Änderungen an der DB nachgezogen werden, müsste ich entweder (a) ein separates Programm schreiben was das macht (wofür? er kriegt doch bereits ein Programm von uns, das das übernehmen könnte?) oder (b) einen Berater rausschicken, der die Änderung an der DB vornimmt. Unsinn. Also gibts eine Update-Funktion, die beim Programmstart die DB darauf überprüft, ob die Änderungen schon vorliegen oder eben nicht und diese dann bei Bedarf direkt vornimmt. Ohne dass der Kunde noch irgendwas anderes vorbereiten oder ein separates Programm starten müsste.
    Weiß nicht, was daran verwerflich sein soll.

    dive26 schrieb:

    Daher würde ich gerne wissen wie ich hiervon den exakten Datentyp rauslesen kann.

    Darauf kann ich dir morgen mit einem Code-Schnipsel antworten.
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.

    nafets3646 schrieb:

    Bitte geh vom CreateObject weg, das ist nicht sauber.

    Hö? Wo siehst du ein "CreateObject"? Was genau meinst du?
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    jo, da habich mich zu extrem ausgedrückt. Also im ursprünglichen Thread schien mir das Datenmodell verfehlt, und der TE wollte gewissermassen Daten eingeben, indem er die Tabellenbreite vergrößerte - so in der Art. Das war dabei mit "zur Laufzeit" gemeint. (Ich hab auch schon Leuten versucht auszureden, pro Kunde eine neue Tabelle inne DB anzulegen - also ich bin ja durchaus Verehrer der Normalisierung ;))
    Wie gesagt: Natürlich ändert sich ein Datenmodell, und wie man die Updaterei aufzieht kann mir ja egal sein - mw. auch mit einem DB-Verbesserungs-Modul inne Client-Anwendung.

    zum vermutlichen Kernthema dieses Threads: Es scheint typisierte Datenobjekte zu geben - keine Datasets, die strukturell analysiert werden sollen, um daraus eine darauf passende DB zu generieren.
    An sowas hab ich auch schon gedacht, natürlich für typisierte Datasetse.
    Da wäre das eine prinzipiell eiglich total triviale Fleiß-Arbeit, das zu coden, eben die Tabellen zu durchlaufen, und je Tabelle die Spalten, und dann nochmal alle DataRelationen, und dabei halt geeignetes Sql zu generieren, was zB eine Access-DB aufsetzen könnte.
    Damit hätte man den Assistenten quasi umgedreht: Statt aus der DB ein Dataset zu generieren, kann man nun zusätzlich auch aus dem Dataset die DB generieren.
    Habich nur noch nicht gemacht, weil dann bestimmt wieder alle möglichen Sql-Dialekte verlangt werden, und ausserdem braucht man das pro Entwicklung einer DB-Anwendung kaum öfter als ein einziges Mal.
    Aber mache ich mal, das Tool.
    Ich müsste halt bisserl mehr Sql lernen, wie man Tabellen Created, Indizees, Constraints, und wie welche .Net-Typen auf welche Access-Datentypen zu mappen sind.
    Klar kannman son Tool auch für selbstgebastelte Klassen schreiben, aber die selbstgebastelten Klassen versagen halt, wenn sie relationale Zusammenhänge abbilden sollen.


    zur Frage konkret:
    Das listet mir die Namen auf und in den Klammern die Werte. Jedoch kann ich da noch nicht unterscheiden ob Integer, Double etc. (siehe Screenshot).
    darf ich wieder mittm ObjectBrowser kommen, ich finde, ein Blick genügt, und man hat die Antwort:
    Bilder
    • Shots02.Png

      84,11 kB, 574×639, 239 mal angesehen

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „ErfinderDesRades“ ()

    ErfinderDesRades schrieb:

    Klar kannman son Tool auch für selbstgebastelte Klassen schreiben, aber die selbstgebastelten Klassen versagen halt, wenn sie relationale Zusammenhänge abbilden sollen.

    Relationale Beziehungen zwischen Tabellen sind in der Tat eine Sache, die man mit dem gewählten Ansatz nicht mit einem Automatismus abdecken kann, aber mit ein wenig zusätzlicher Intelligenz in den Datenklassen dürften die meisten üblichen Fälle auch machbar sein. Mal sehen wie meine Bibliothek sich in den zwei demnächst geplanten Projekten, in denen sie zum Einsatz kommen soll, schlagen wird und was ggf. noch verbesserungswürdig ist.
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    Ich bin selbstgebastelten Daten-Klassen ziemlich abgeneigt, weils ein irrwitziger Aufriss ist, auch nur annähernd die Leistungsfähigkeit eines typDatasets hinzukriegen.
    Damit meine ich die Unterstützung von Databinding, Sortierung, Filterung, und Änderungsverfolgung.
    Beim typDataset kannst du dir einen Batzen Daten laden (zB 20 Kunden und 1000 Bestellungen), und mw. 30 Änderungen verschiedenster Art und an ganz verschiedenen Stellen vornehmen.
    Anschließend fährst du die zentrale Speicher-Routine ab, und die DataAdapter senden nur genau die geänderten Datensätze an die DB - nicht mehr.
    In vier Views-Videos kannst du gucken, wie man in vlt. 15min einen m:n - View hinkloppt. Das ist da zwar ohne DB vorgeführt, aber bei gegebener DB geht das sogar noch bischen schneller, dank der Assis.
    m:n - View ist mir gewissermaßen eine Messlatte für die Leistungsfähigkeit alternativer Vorgehensweisen. Und tatsächlich ist mir noch kein einziges CodeSample untergekommen, was sowas überhaupt mal bewerkstelligt hätte.
    Was meinst du: langt dein Instrumentarium, um in etwa sowas hinzukriegen wie die Sample-Solution von die relationale GrundIdee?

    ErfinderDesRades schrieb:

    langt dein Instrumentarium, um in etwa sowas hinzukriegen wie die Sample-Solution von die relationale GrundIdee?

    Wie gesagt... das wird sich mit einem der nächsten beiden Projekte zeigen, weil mind. bei einem davon sehr viele m:n-Beziehungen vorkommen werden. Ich bin recht zuversichtlich, dass das machbar ist, aber vielleicht reden wir in ein, zwei Monaten nochmal darüber ;)
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    darf ich wieder mittm ObjectBrowser kommen, ich finde, ein Blick genügt, und man hat die Antwort:
    VB.Net Profis vielleicht. Ich weis damit genau so wenig wie vorher ;)

    Hast Du nicht eine "praktische" Zeile Code welche ich in meinen oben geposteten einfüge und die die gewünschte Info hergibt?
    Liebe Grüße
    Roland Berghöfer

    Meine aktuellen und kostenlos verwendbaren Tools (mit VB.NET erstellt): freeremarkabletools.com | priconman.com | SimpleCalendar | AudibleTouch | BOComponent.com | bonit.at

    dive26 schrieb:

    Hast Du nicht eine "praktische" Zeile Code welche ich in meinen oben geposteten einfüge und die die gewünschte Info hergibt?

    Er meint vermutlich FieldInfo.FieldType, welches dir die Datentypen deiner Klassenmember verrät. Wie gesagt, ich hab im Büro einen Schnipsel, womit man daraus den (meist - ist noch nicht ganz perfekt) passenden DBType generieren kann (in meinen Beispielen oben taucht der schon als GetSqlType() auf).
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    Hier übrigens mein Versuch, aus einem FieldType den zugehörigen DbType zu ermitteln:
    Spoiler anzeigen

    C-Quellcode

    1. public String GetSqlTypename(Type type, long length = 0)
    2. {
    3. if (dbTypes.Count == 0)
    4. {
    5. // nachfolgender Aufruf liefert - vereinfacht gesagt - das Ergebnis von
    6. // _connection.GetSchema("DataTypes")
    7. DataTable schema = GetSchema(DefaultSchemaTypes.DataTypes);
    8. if (schema != null)
    9. {
    10. foreach (DataRow row in schema.Rows)
    11. {
    12. try
    13. {
    14. DbFieldTypeInfo fti = new DbFieldTypeInfo();
    15. fti.SysType = (string)row["DataType"];
    16. fti.DbType = (string)row["TypeName"];
    17. fti.isLong = (bool)row["IsLong"];
    18. fti.maxLength = (long)row["ColumnSize"];
    19. dbTypes.Add(fti);
    20. }
    21. catch (Exception ex)
    22. {
    23. throw new DataException("Beim Lesen der Daten aus der Schematabelle der verfügbaren Datentypen ist ein Fehler aufgetreten.", ex);
    24. }
    25. }
    26. }
    27. else
    28. {
    29. throw new DataException("Schema-Informationen zu den von der Datenbank unterstützten Datentypen konnten nicht abgerufen werden.");
    30. }
    31. }
    32. String result = string.Empty;
    33. List<DbFieldTypeInfo> found = dbTypes.FindAll(fti => { return fti.SysType == type.FullName; });
    34. if (found.Count > 0)
    35. {
    36. result = found[0].DbType;
    37. foreach (var ff1 in found)
    38. {
    39. if (ff1.isLong)
    40. {
    41. result = ff1.DbType;
    42. }
    43. else if (length > 0 && length <= ff1.maxLength)
    44. {
    45. result = string.Format("{0}({1})", ff1.DbType, length);
    46. break;
    47. }
    48. }
    49. }
    50. return result;
    51. }

    Dazu muss ich anmerken, dass es in .NET grundsätzlich keine Strings fester Länge mehr gibt (so wie in VB6). Das bedeutet aber auch, dass Strings "eigentlich" beliebig lang werden können und damit auch ausreichend Platz in der Tabellenspalte verfügbar sein sollte, d.h. daraus wird sowas wie ein Memo oder LongText, was aber in den meisten Fällen gar nicht erforderlich, gewünscht oder sinnvoll ist. Dazu benutze ich eben das bereits erwähnte eigene DatabaseFieldAttribute, bei dem ich in solchen Fällen auch die Breite eines Strings für die Datenbank festlegen kann (im Code repräsentiert durch den Funktionsparameter length).

    Zu deiner Struktur ist mir noch was aufgefallen:

    dive26 schrieb:

    VB.NET-Quellcode

    1. Dim BenutzerBild As String 'serialisiertes Bild
    2. Dim Finger1 As String 'serialisierte biometrische Information
    3. Dim Finger2 As String 'serialisierte biometrische Information
    4. Dim Finger3 As String 'serialisierte biometrische Information

    Ich würd's nicht in einen String packen, immerhin willst du hier offensichtlich keine Texte, sondern binäre Rohdaten speichern. In VB6 hab ich mich in solchen Fällen auch bevorzugt mit Strings abgegeben, weil es in vielen Fällen praktischer war vom Handling her, aber in .NET ist es deutlich günstiger und einfacher zu warten, wenn man hier von vornherein ein Byte-Array (Byte()) benutzt.
    Die o.g. Funktion zum ermitteln des Db-Typs kommt damit sogar wunderbar zurecht und hat in meiner Test-Access-DB daraus wie gewünscht ein Memo-Feld gemacht.
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.