Datenbank-Schema auslesen - DataViewer

    • VB.NET

      Datenbank-Schema auslesen - DataViewer

      Jeder Datenbank-Anbieter stellt sein eigenes Instrumentarium bereit, mit dem auf die DB zugegriffen werden kann.
      Das Instrumentarium ist gewissermaßen "normiert", durch gemeinsame Basisklassen und Schnittstellen. Daher kann - obwohl alle DB-Systeme unterschiedlich ticken, mit einer gewissen Einheitlichkeit vorgegangen werden.

      Dieser DatabaseViewer nutzt zwei Sachen, um Datenbanken möglichst vieler Datenbanksysteme zu explorieren:
      • die Tatsache, dass jeder DB-Anbieter auch eine DBProviderFactory bereitstellt, mit der man sich das weitere Instrumentarium für den DB-Zugriff generieren lassen kann.

      • die Connection.GetSchema() - Methode
        Connection.GetSchema() gibts in zwei Überladungen, die jeweils eine DataTable zurückgeben:
        1. Connection.GetSchema() ohne Parameter ruft das Grund-Schema ab, welches überhaupt angibt, welche Schemata abgerufen werden können. Das mag so aussehen:

          Erkennbar, dasses sich um einen DBProvider handelt, der eine Rechte-Verteilung unterstützt (Schema "users"), und auch stored Procedures, inklusive Parametern (MySql).

        2. Dem Grundschema kann man die Namen der verfügbaren Schemata entnehmen, und kann diese dann mit der parametrisierten Überladung von .Getschema(schemaName) weiter abfragen - der Abruf von Connection.GetSchema("DataTypes") liefert für MySql zB folgende Information:

          (Ich habe die Spaltenbreiten so schmal wie möglich gemacht, weil es gibt u.U. sehr viele Spalten - und mit den Tooltips kommt man zur Not ja auch hin)

      Funktioniert für MySql, Access, SqlServer, Excel und Csv.
      Oracle und weitere Anbieter sind nicht getestet, aber wenns mit rechten Dingen zu geht, darfs bei professionellen Anbietern eiglich kein Problem geben.
      Für SqLite funktionierts leider nur mit Framework3.5, denn SqLite stellt, soweit ich weiß, noch keine Unterstützung für FW4 bereit (Stand 3-2012).
      Für SqLite muß man die dem Framework entsprechende Version der Dll eingebunden haben
      SqlServerCe wird erstaunlicherweise nicht unterstützt: NotSupportedException erfolgt beim Abruf des Grund-Schemas, was ich für ein haus-eigenes Produkt von MS ziemlich schwach finde :(

      Unter den verfügbaren Schemata (Datentypen, User-Rechte, Indexe, Constraints, Procedures,...) ist natürlich das Tabellen-Schema von herausragender Bedeutung.
      Deshalb entnimmt DataViewer gleich beim Aufruf dem Tabellen-Schema die verfügbaren Tabellen-Namen, und präsentiert sie in einer weiteren Listbox (die erste Listbox präsentiert die verfügbaren Schemata).

      Aus diesen Tabellen-Namen kann man ja leicht eine Sql-Query bauen, die dann die Tabellen-Daten abruft (das müsster mir jetzt ohne Bildchen abkaufen ;)).
      Die Darstellung im untypisierten DatagridView ist natürlich grauenhaft, aber es ist ja nur ein Tool, um einen Überblick zu gewinnen, wie eine unbekannte DB aufgebaut ist, und ob ühaupt Daten da sind.

      Code
      frmDataViewer hat eine Sub New, die eine ConnectionFactory erwartet, und beliebig viele String-Segmente, die zum ConnectionString zusammengesetzt werden. In Sub New werden zwei List(Of String) befüllt: die eine mit dem Grund-Schema (Connection.GetSchema()), und die andere mit den Tabellen-Namen (Connection.GetSchema("Tables")) (Zeilen #14 - #17).

      VB.NET-Quellcode

      1. Public Class frmDataViewer2008
      2. Private _SchemaNames As New List(Of String)
      3. Private _TableNames As New List(Of String)
      4. Private _Con As DbConnection
      5. Private _Factory As DbProviderFactory
      6. Public Sub New(ByVal fct As DbProviderFactory, ByVal ParamArray sCon() As String)
      7. MyClass.New()
      8. _Factory = fct
      9. _Con = fct.CreateConnection
      10. _con.ConnectionString = String.Concat(sCon)
      11. _con.Open()
      12. Using generalSchema = _con.GetSchema(), tableSchema = _con.GetSchema("Tables")
      13. _SchemaNames.AddRange(From rw In generalSchema Select Name = rw(0).ToString)
      14. _TableNames.AddRange(From rw In tableSchema Select Name = rw("TABLE_NAME").ToString)
      15. End Using
      16. _Con.Close()
      17. lstSchema.DataSource = _SchemaNames
      18. lstTable.DataSource = _TableNames
      19. AddHandler lstSchema.SelectedIndexChanged, AddressOf DbTable_SelectedIndexChanged
      20. AddHandler lstTable.SelectedIndexChanged, AddressOf DbTable_SelectedIndexChanged
      21. DbTable_SelectedIndexChanged(lstSchema, EventArgs.Empty)
      22. End Sub
      Zum Schluß werden 2 Listboxen an die Listen gebunden, und deren SelectedIndexChanged-Event wird abonniert (#19 - #22).

      Im SelectedIndexChanged-Handler wird nun je nach auslösender Listbox entweder eine DataTable mit einem Schema erstellt oder ein DataAdapter kriegt ein SelectCommand, mit dem eine bestimmte DB-Tabelle abgerufen wird.
      Issn bischen aufwändig gemacht, weil ich bischen Fehler-Catching mache, und auch ins Debug-Fenster logge - ich hoffe, ihr blickt trotzdem durch:

      VB.NET-Quellcode

      1. Private Sub DbTable_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)
      2. If lstTable.SelectedIndex < 0 Then Return
      3. Dim tb = TryCast(Grid.DataSource, DataTable)
      4. If tb IsNot Nothing Then tb.Dispose()
      5. Grid.DataSource = Nothing
      6. _Con.Open()
      7. Select Case True
      8. Case sender Is lstSchema
      9. Dim schema = _SchemaNames(lstSchema.SelectedIndex)
      10. Dim msg = "Connection.GetSchema(""" & schema & """)"
      11. Debug.Write(msg & " ")
      12. Try
      13. Grid.DataSource = _Con.GetSchema(schema)
      14. Debug.WriteLine("")
      15. Catch ex As Exception
      16. Debug.WriteLine("Failed: " & ex.Message)
      17. MessageBox.Show(msg & " konnte nicht ausgeführt wern")
      18. End Try
      19. Case sender Is lstTable
      20. tb = New DataTable
      21. Dim sTable = _TableNames(lstTable.SelectedIndex)
      22. Using adp = _Factory.CreateDataAdapter, cmd = _Factory.CreateCommand
      23. adp.SelectCommand = cmd
      24. cmd.Connection = _Con
      25. cmd.CommandText = "Select * from `" & sTable & "`"
      26. Debug.Write(cmd.CommandText & " ")
      27. Try
      28. adp.Fill(tb)
      29. Debug.WriteLine("")
      30. Catch ex As Exception
      31. cmd.CommandText = "Select * from [" & sTable & "]"
      32. Debug.Write(cmd.CommandText & " ")
      33. Try
      34. adp.Fill(tb)
      35. Debug.WriteLine("")
      36. Catch ex2 As Exception
      37. Debug.WriteLine("Failed: " & ex2.Message)
      38. MessageBox.Show("""" & cmd.CommandText & """: Abfrage konnte nicht ausgeführt wern")
      39. End Try
      40. End Try
      41. Grid.DataSource = tb
      42. End Using
      43. End Select
      44. _Con.Close()
      45. End Sub


      Das war auch schon das ganze Form, und der eigentliche Code des Viewers. Kann man aufrufen wie jedes Form, und wenn man die Sub New nimmt, welche DbProviderFactory und Connectionstring entgegennimmt, kann man halt inne DB browsen - sowohl in den Schemata als auch in den Daten.
      Inne Sample-App rufe ich das Form aus einer Sub Main auf, also das VB-AnwendungsFramework ist in diesem Sample in den Projekt-Einstellungen deaktiviert - steht natürlich frei, es auch anders zu halten.
      In Sub Main jedenfalls findet sich folgender komischer Code:

      VB.NET-Quellcode

      1. <STAThread()> _
      2. Public Sub Main(ByVal commandLineArgs As String())
      3. Application.EnableVisualStyles()
      4. Application.SetCompatibleTextRenderingDefault(False)
      5. Dim frm As frmDataViewer2008 = Nothing, dataPath = ""
      6. If True Then
      7. dataPath = Path.GetFullPath("..\..\Data\OrderDB.s3db")
      8. frm = New frmDataViewer2008(SQLite.SQLiteFactory.Instance, "Data Source=", dataPath)
      9. ElseIf True Then
      10. dataPath = Path.GetFullPath("..\..\Data\Kartenliste WoW TCG Starter.xls")
      11. frm = New frmDataViewer2008(OleDbFactory.Instance, _
      12. "Provider=Microsoft.Jet.OLEDB.4.0; Extended Properties=""Excel 8.0""; Data Source='", dataPath, "';")
      13. ElseIf True Then
      14. dataPath = Path.GetFullPath("..\..\Data\Fotos.mdb")
      15. frm = New frmDataViewer2008(OleDbFactory.Instance, _
      16. "Provider=Microsoft.Jet.OLEDB.4.0;Persist Security Info=True; Data Source='", dataPath, "';")
      17. ElseIf True Then
      18. dataPath = Path.GetFullPath("..\..\Data\Csv")
      19. frm = New frmDataViewer2008(OleDbFactory.Instance, _
      20. "Provider=Microsoft.Jet.OLEDB.4.0; Extended Properties=""text;HDR=Yes;FMT=Delimited""; Data Source='", dataPath, "';")
      21. ElseIf True Then
      22. 'SqlCe unterstützt keinen Abruf von Schema-Informationen
      23. dataPath = Path.GetFullPath("..\..\Data\Bestellungen.sdf")
      24. frm = New frmDataViewer2008(SqlCeProviderFactory.Instance, "Data Source='", dataPath, "';")
      25. ElseIf True Then
      26. 'MySql auf db4Free: Passwort disabled
      27. frm = New frmDataViewer2008(MySqlClientFactory.Instance, _
      28. "server=w00cb7ca.kasserver.com;user id=XXXXXX;password=XXXXXXX;persist security info=True;database=XXXXXX")
      29. ElseIf True Then
      30. End If
      31. Application.Run(frm)
      32. My.Settings.Save()
      33. End Sub
      Also eine total bescheuerte ElseIf - Kette, von der nur der erste Zweig zur Ausführung kommt.
      Das ist Absicht, und praktisch zum Experimentieren: Man kann einfach zB die Zeilen #10-#13 an die Position #7 verschieben, und statt in der SqLite-DB wird dann eben in einem Excel-Workbook gebrowst.
      Grad beim Zugriff auf Excel mittels OleDB ist das Tool recht nützlich, weil da kann man sehen, wie die Daten vom DBProvider aufgefasst werden - ein Excel-Worksheet ist ja meist nicht wirklich kompatibel.
      Auch die Abfrage von Csv ist interessant - in zweifacher Hinsicht:
      1. Zunächst mal ist zu beachten, dass beim Csv-Abruf nicht eine Datei im ConnectionString anzugeben ist, sondern
        Alle Csv-Dateien eines Directories werden gemeinsam als Datenbank aufgefasst.

      2. Weiters ist wesentlich, was in der Registry als Csv-Delimiter (Spaltentrenner) angegeben ist. Bei mir zB. ist "," der Delimiter, und deshalb ist meine Csv-Ansicht Schrott, weil die Daten wurden aus Excel exportiert, und das verwendete ";" als Delimiter.
      Letztere Finesse ist auf connectionstrings.com/textfile dokumentiert - sehr empfehlenswerte Site.

      (Die Downloads sind übrigens so fett, v.a., weil ich Sample-DBs mit drinne hab - Code ist ja nicht so viel ;).)
      Dateien
      • DataViewer2008.zip

        (789,96 kB, 761 mal heruntergeladen, zuletzt: )
      • DataViewer.zip

        (776,62 kB, 1.173 mal heruntergeladen, zuletzt: )

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