Typ. Dataset und DB, die noch gestartet werden muß

  • C#
  • .NET (FX) 4.0

Es gibt 13 Antworten in diesem Thema. Der letzte Beitrag () ist von OlafSt.

    Typ. Dataset und DB, die noch gestartet werden muß

    Hallo Freunde,

    ich habe nun ein ernstzunehmendes Projekt in C' zu fassen und möchte die DB-Zugriffe gern via typisierte Datasets regeln. Da gibts nun ein kleines Hindernis.

    DB-Server ist SQL Anywhere. Der Zugriff auf die Datenbanken ist da etwas merkwürdig, denn im Gegensatz zu den SQL-Servern die ich bisher zu fassen hatte, werden dort Datenbanken nicht allesamt gemountet. Bei SQL-Anywhere werden DBs entladen, sobald die letzte Connection getrennt wurde. Was zur Folge hat, das ich erstmal die Datenbanken alle mounten muß - das habe ich problemlos mit OleDBConnection und OleDbCommands hinbekommen.

    Doch wie bringe ich das meinem typisierten Dataset bei ? Mein bevorzugter Weg wäre, via Oldschool-OleDbCommands die DBs zu starten und dann das typisierte Dataset dranzulassen.
    vlt. ein Extra-Modul schreiben, was zu alle "die Datenbanken" je eine Connection öffnet, und sonst nix tut.
    Dann - mit anderen Connections - Work as Usual: nur zum Zugriff öffnen.

    Wenn du DataAdapter verwendest, kannst du auch die geöffneten Connections des Moduls verwenden - ein DataAdapter macht die Connection nur dann hinter sich zu, wenn er sie auch selbst öffnen musste.

    Warum "die Datenbanken" - normal reicht doch eine Db reichlich aus?
    Prinzipiell hast du recht, eine DB sollte reichen. In meinem Fall muß ich Daten aus einer Finanzsoftware fischen, während meine Anwendung noch einen Extrasatz an Daten hat, die in einer Finanzsoftware einfach keinen Platz haben. Der mit der Finanzsoftware mitgelieferte SQL-Server - eben SQL Anywhere - wird von uns dreist mitbenutzt ;)
    Okay, ich bekomms nicht so einfach hin - weil mich auch das VS da echt im Stich läßt. Ich benutze VS2013 Community und lasse mein Programm gegen .NET 4.0 Full kompilieren.

    Wie schon erwähnt, habe ich es mit mehreren DBs einer Finanzsoftware zu tun. DB1 muß ich "zu Fuß" mit OleDBConnection/OleDbCommand bearbeiten. Über die Commands ermittle ich Usernamen und Passwort für den Zugriff auf DB2 der Finanzsoftware. Außerdem haben wir da unsere DB3, dies ist eine von mir erstellte DB, die einfach in den SQL-Server der Finanzsoftware gemountet wird.

    Nun muß ich dem TableAdapter für DB2 einen neuen Connectionstring verpassen, schließlich muß ich im ConnectionString die Werte für "UserID", "Password" und "Datasource" austauschen. Den Basisanteil des ConnectionStrings ("DatabaseName", "ServerName") ist im app.config festgelegt und kann auch problemlos ausgelesen werden.

    C#-Quellcode

    1. string s = Properties.Settings.Default.CompanyConnString;

    Ich modifiziere diesen String nun und ergänze ihn um die ermittelten Werte:

    C#-Quellcode

    1. s += ";Password=" + U0PWD + ";Datasource="+U0MachineName;


    So weit so gut. Nun muß ich meinem TableAdapter diesen neuen ConnectionString zuweisen:

    C#-Quellcode

    1. fK_KundeTableAdapter.Connection.ConnectionString = s;


    Klappt nicht. Ich bekomme keine Exception, keine Fehlermeldung, nichts. Sämtliche Codezeilen werden vom Debugger übersprungen (oder in Echtzeit ausgeführt) und das normale, leere Anwendungsfenster erscheint.

    Auch ein

    C#-Quellcode

    1. fK_KundeTableAdapter.Connection.Close();
    2. fK_KundeTableAdapter.Connection.ConnectionString = s;
    3. fK_KundeTableAdapter.Connection.Open();


    hat nichts geändert. Beende ich das Programm dann, stoppt der Debugger in einem neuen Fenster, Datei Main.Designer.cs, Methode "Dispose(bool disposing)". Dieses nervtötende Verhalten kenne ich sonst nur vom leicht fehlerhaften Delphi-Debugger, der z.B. nicht in Exceptionhandler springen kann, sondern einfach das Programm durchrasen läßt.

    Ich habe es auch schon mit einem SqlConnectionStringBuilder versucht, mit identischen Ergebnissen:

    C#-Quellcode

    1. SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(Properties.Settings.Default.CompanyConnString);
    2. //auch nicht besser;
    3. //string s = Properties.Settings.Default.CompanyConnString;
    4. //SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(s);

    beides läßt das Programm wie geschildert durchrauschen.

    Frage 1: Was mache ich falsch ?
    Frage 2: Wie mache ich es richtig ?
    probier erstmal, es ohne Manipulationen am Connectionstring hinzukriegen.
    Generierte TableAdapter sperren sich sehr dagegen, sich eine geänderte Connection unterjubeln zu lassen. Das hat sich glaub zwischenzeitlich verbessert, aber zu den Zeiten, wo ich die Dinger noch verwendete, war ein sehr abgedrehter Workaround nötig, der zum Startup an den Settings herummanipuliert hat und sowas.
    Ohne einen veränderten ConnectionString geht es nicht, da ichm wie schon gesagt, erst die UserID und das Passwort zur Laufzeit ermitteln kann.

    Ich habe auch selbst noch ein wenig herumgeforscht und dabei herausgefunden: Den ConnectionString anpassen ist leider nicht drin. Damit ist, leider leider leider, das typisierte Dataset für dieses Projekt gestorben. Mist verdammter X(

    Kommen wir zur nächsten Frage: Was nehme ich stattdessen ? Ehrlich gesagt ist es mir noch etwas rätselhaft, wie ich mit OleDbCommands ein DGV füllen kann, ohne selbst die Daten ins DGV kippen zu müssen... Bin für heiße Ideen offen :)

    OlafSt schrieb:

    Den ConnectionString anpassen ist leider nicht drin.
    warum nicht?
    Ist doch letztlich ein String wie jeder andere.

    Habich ja schon erwähnt: typisierte TableAdapter verwende ich überhaupt nicht, sondern ich konfiguriere mir meine DataAdapter codeseitig, auch mit Hilfe von CommandBuilder-Objekten. Da kann ich jederzeit einen ConnectionString festlegen, ganz wie mir beliebt.
    So, ich habe etwas weitergeforscht. Leider ist das ganze echt mühsam, denn sämtliche Tutorials, denen ich folgen kann, gehen über den Dataset-Designer (den ich in meinem Fall nicht verwenden kann). Selbst die hervorragenden Tuts von dir, lieber EDR, laufen nur über den Designer - oder arbeiten mit ReadXML/WriteXML. Auch wie die einzelnen Objekte (DataSet, DataAdapter etcpp) zusammenhängen, ist nirgendwo wirklich durchschaubar dargestellt. MSDN ist fast völlig unverständlich, weil sie sich auch mit den ganzen Spezial- und Sonderfällen befassen.

    Immerhin habe ich herausgefunden, das ich eine OleDbConnection haben muß - wußte ich schon zu Anfang. Doch wie weiter ? Ich habe mir dann ein Dataset passend zu meiner Tabelle manuell erzeugt (im Designer) und instantiiere es. Irgendwo habe ich dann gelesen, das man das DataSet mittels einem DataAdapter füllen muß. Auch das habe ich hingebastelt und die .Fill-Methode bombt mir nicht mit einer Fehlermeldung raus - muß also schon mal was klappen.

    Doch ob ich Daten im DS habe ? Keinen Schimmer. Wie binde ich das DataSet nun an ein DataGridView ? Ich hab was von BindingSource gelesen, doch meine Versuche enden allesamt in einem leeren DGV.

    Hier meine Versuche so weit:

    C#-Quellcode

    1. //Korrekte Connectionstrings aufbauen - LXCOMPANY
    2. string CompanyStr = Properties.Settings.Default.CompanyConnString;
    3. CompanyStr += ";Provider=SAOLEDB.12;Password=" + dbc.U0PWD + ";Datasource=" + Properties.Settings.Default.HostName;
    4. //Bootsdatenbank
    5. string BootStr = Properties.Settings.Default.BootConnString;
    6. BootStr += ";Provider=SAOLEDB.12;UserID=***;Password=***;Datasource=" + Properties.Settings.Default.HostName;
    7. //Connections herstellen
    8. dbc.SetupConnections(CompanyStr, BootStr); //Works perfectly
    9. //dbc.BootConn ist die im folgenden verwendete OleDbConnection
    10. //DataAdapter für Bootsdatenbank erzeugen
    11. BootsDBDS = new BootsDBDataset();
    12. OleDbDataAdapter BootDBAdapter = new OleDbDataAdapter("SELECT * FROM Boot", dbc.BootConn);
    13. BootDBAdapter.Fill(BootsDBDS); //Keine Fehlermeldung
    14. //Damit das ganze im DGV landet, brauchts noch eine BindingSource ?!?
    15. BindingSource DGVBS = new BindingSource();
    16. DGVBS.DataSource = BootsDBDS;
    17. dGV1.DataSource = DGVBS;
    18. //DGV bleibt leer :-(
    in meiner Welt brauchst du nur eine funktionierende DBConnection. Hast du die, kannst du mit typisierten Datasets arbeiten, und zum Konfigurieren von DataAdaptern nehme ich meine Dlls - es sind mehrere.
    DBExtensions - allgemeine Lösung der Daten-Persistierung via Datenbanken

    Wie sich das allerdings mit deim komischen Sql-AnyWhere verträgt weissichnich.
    Spielen wir das Spiel einmal anders herum.

    Nehmen wir einen SQL-Server von Microsoft und seine Northwind-Datenbank. Nehmen wir weiter an, das der Kunde der integrierten Windows-Authentifizierung nicht vertraut - wir müssen also SQL-Server-Authentifizierung verwenden. Außerdem ändert der Administrator des Kunden aufgrund gesteigerter Paranoia zufällig die Kennwörter der Leute, die nach dem fehlgeschlagenen Login bei ihm das neue Kennwort bekommen.

    Dieses Szenario ist mit den Bordmitteln von .NET eigentlich vollkommen unlösbar. Denn alle bisher beschrittenen Wege über typisierte Datasets, TableAdapter und all das gedöns (inklusive Entity Framework) setzen eines voraus: Das der ConnectionString bereits sämtliche Informationen enthält, inklusive Benutzernamen und Kennwort für das Login zum SQL-Server. Spaßig daran ist ja, das Benutzername und Kennwort im Klartext in einer Textdatei stehen - in einer Behörde wäre das ein absolutes No-Go.

    Wie schaffe ich es also, einen Login.Dialog einzublenden, Benutzernamen und Kennwort für das Login zum SQL-Server abzufragen und dann erst meine Datasets anzuwerfen ? Bedingung ist, das weder Username noch Kennwort irgendwie in irgendeiner Datei gespeichert werden dürfen.

    Dieses Szenario muß sich doch lösen lassen.
    Vorab: Die DBExtensions arbeiten problemlos mit SQLAnywhere, die Factory ist iAnywhere.Data.SQLAnywhere.SAFactory.
    Ich bin nach wie vor zu blöd:

    C#-Quellcode

    1. string Str = Properties.Settings.Default.BootConnString;
    2. Str += ";UserID=***;Password=***";
    3. //Dataset erzeugen
    4. DBDS = new BootsDBDataset();
    5. //DataAdapter erzeugen
    6. //Versuchen wir es mit EDRs Extensions
    7. try
    8. {
    9. adp = new DatasetAdapter(iAnywhere.Data.SQLAnywhere.SAFactory.Instance, Str, ConflictOption.OverwriteChanges);
    10. }
    11. catch { throw; }
    12. DBDS.Adapter(adp);
    13. bootsdatenBindingSource.DataSource = DBDS;
    14. bootsdatenBindingSource.DataMember = "Bootsdaten";
    15. DBDS.Fill();


    Compiliert fehlerfrei, läuft ohne Probleme durch. Aber mein DatagridView ist immer noch leer. DataGridView1.DataSource zeigt auf bootsdatenBindingSource; bootsdatenBindingSource.Datasource zeigt auf den manuell erstellten DBDS, bootsdatenBindingSource.DataMember zeigt auf die Tabelle "Bootsdaten".

    Ich kapier das offengestanden nicht mehr. Wo ist der Haken in dem ganzen ?

    Versuche ich manuell durch das Dataset und seine Tabellen zu wandern, rummst es gleich wieder:

    C#-Quellcode

    1. var dr = DBDS.CreateDataReader(DBDS.Bootsdaten);
    2. string s = "";
    3. while (!dr.Read())
    4. {
    5. try
    6. {
    7. s = dr["KDNR"].ToString(); //Hier rummst es
    8. }
    9. catch { throw; }
    10. MessageBox.Show(s);
    11. }
    12. dr.Close();


    Fehlermeldung: DataTableReader ist für die aktuelle DataTable 'Bootsdaten' ungültig ;(

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „OlafSt“ ()

    evtl. ist der Denkfehler in Listing#1, zeile#4: In meim konzept typisierter Datasets ists ausserordentlich unerwünscht, zur Laufzeit Datasetse zu erzeugen.
    Weil man die im Designer aufs Form zieht, und allerlei Databindings daran einrichtet.
    Wenn du nun zur Laufzeit weitere Datasetse erzeugst - dein DGV ist nicht an die gebunden, und wird daher nicht viel anzeigen.
    Das DGV ist ja ans designer-erzeugte Dataset gebunden, und's gibt normal auch kein Grund, das zu ändern.

    vier Views-Videos

    Übrigens die Exception fliegt auch genausogut, wenn du den TryCatch weglässt.

    ErfinderDesRades schrieb:


    Übrigens die Exception fliegt auch genausogut, wenn du den TryCatch weglässt.


    Nicht bei mir. Ohne TryCatch rast das Programm sofort in Echtzeit weiter. Kein 20sekündiges warten auf die Exception-Box, die mir dann sehr ausführlich mitteilt, was ich falsch gemacht habe. Die bekomme ich erst mit dem TryCatch über das X im roten Kreis ;)

    Inzwischen habe ich mein Problem in den Griff bekommen, nachdem ich mir auf dem kurzen Dienstweg das Buch "Datenbankprogrammierung mit Visual C# 2012" besorgt habe. EBooks sind doch was feines ;) In diesem Buch fand ich dann endlich die Grundlagen, nach denen ich seit Tagen suche und Frage. Verschiedenes ist dabei plötzlich klar geworden und puff - plötzlich habe ich Daten im DGV, mit meinen selbsterstellten Datasets und Commands und derlei Zeugs.

    Womöglich mache ich mal ein Datenbank-Tutorial für "alte Anfänger-Hasen", die schon seit Jahren mit Datenbanken nach dem "alten" ADO-Modell arbeiten und nun auf ähnliche Probleme mit dem "neuen" ADO.NET-Modell stoßen.

    Bis hierher erstmal 1000 Dank für deine Geduld mit mir :thumbup: