Problem mit/bei OOP bzgl. MySQL + MSSQL

  • VB.NET
  • .NET (FX) 4.5–4.8

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

    Problem mit/bei OOP bzgl. MySQL + MSSQL

    Hallo,

    ich versuche mich gerade an einer Lösung um schnell und einfach Abfragen in MySQL und MSSQL zu realisieren. Früher hatte ich immer eine Art globale Variable in der steht, ob es MySQL oder MSSQL ist, entsprechend habe ich dann immer eine neue Instanz der jeweiligen Klasse erstellt. Dafür musste ich aber ständig if-Abfragen einbauen und das kann keine Lösung sein, vor allem nicht bei einem größeren Projekt was ich derzeit anstrebe. Daher habe ich mal mit meinem (noch geringen Wissen) mit OOP an die Sache heran gewagt.

    Gegeben ist folgendes:
    Eine Form, auf der die verschiedenen Einstellen getätigt werden (Server, Nutzername, Passwort, etc...). Das soll alles mittels DataBinding geschehen. Diese Einstellungen werden am Ende serialisiert und sind somit jederzeit aufrufbar. Ich habe mir jetzt folgende Klassen erstellt:


    SqlVerbindung.vb (Basisklasse):

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Data.Common
    2. <Serializable>
    3. Public MustInherit Class SqlVerbindung
    4. Enum SqlDataType
    5. VarChar
    6. Text
    7. Int
    8. End Enum
    9. Protected AbfragenMY As New Dictionary(Of String, String) From
    10. {
    11. {"Datenbanken", "SHOW DATABASES"}
    12. }
    13. Protected AbfragenMS As New Dictionary(Of String, String) From
    14. {
    15. {"Datenbanken", "SELECT name FROM master.dbo.sysdatabases"}
    16. }
    17. Private _Server As String
    18. Public Property Server As String
    19. Get
    20. Return _Server
    21. End Get
    22. Set(value As String)
    23. _Server = value
    24. End Set
    25. End Property
    26. Private _Benutzer As String
    27. Public Property Benutzer As String
    28. Get
    29. Return _Benutzer
    30. End Get
    31. Set(value As String)
    32. _Benutzer = value
    33. End Set
    34. End Property
    35. Private _Passwort As String
    36. Public Property Passwort As String
    37. Get
    38. Return _Passwort
    39. End Get
    40. Set(value As String)
    41. _Passwort = value
    42. End Set
    43. End Property
    44. Private _Datenbank As String
    45. Public Property Datenbank As String
    46. Get
    47. Return _Datenbank
    48. End Get
    49. Set(value As String)
    50. _Datenbank = value
    51. End Set
    52. End Property
    53. Private _Art As String
    54. Public Property Art As String
    55. Get
    56. Return _Art
    57. End Get
    58. Set(value As String)
    59. _Art = value
    60. End Set
    61. End Property
    62. Private _Port As UShort = 3306
    63. Public Property Port As UShort
    64. Get
    65. Return _Port
    66. End Get
    67. Set(value As UShort)
    68. _Port = value
    69. End Set
    70. End Property
    71. Private _IntegratedSecurity As Boolean
    72. Public Property IntegratedSecurity As Boolean
    73. Get
    74. Return _IntegratedSecurity
    75. End Get
    76. Set(value As Boolean)
    77. _IntegratedSecurity = value
    78. End Set
    79. End Property
    80. Protected MustOverride Function BekommeVerbindung() As DbConnection
    81. Protected MustOverride Function SqlSelect(ByVal Name As String, Optional ByVal Parameter As DbParameter() = Nothing) As DataTable
    82. Protected MustOverride Function ErstelleParameter(ByVal Name As String, ByVal Value As Object, ByVal Typ As SqlDataType) As DbParameter
    83. Protected MustOverride Sub SqlUpdate()
    84. End Class


    MySqlVerbindung.vb:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Data.Common
    2. Imports Devart.Data.MySql
    3. <Serializable>
    4. Public Class MySqlVerbindung
    5. Inherits SqlVerbindung
    6. Protected Overrides Sub SqlUpdate()
    7. Throw New NotImplementedException()
    8. End Sub
    9. Protected Overrides Function SqlSelect(Name As String, Optional Parameter() As DbParameter = Nothing) As DataTable
    10. Using con = BekommeVerbindung()
    11. Using cmd As New MySqlCommand(AbfragenMY(Name), con)
    12. If Parameter IsNot Nothing Then
    13. cmd.Parameters.AddRange(Parameter)
    14. End If
    15. Using ada As New MySqlDataAdapter(cmd)
    16. Using dt As New DataTable
    17. ada.Fill(dt)
    18. Return dt
    19. End Using
    20. End Using
    21. End Using
    22. End Using
    23. End Function
    24. Protected Overrides Function ErstelleParameter(Name As String, Value As Object, Typ As SqlDataType) As DbParameter
    25. Dim parameter As New MySqlParameter()
    26. With parameter
    27. .ParameterName = Name
    28. .Value = Value
    29. Select Case Typ
    30. Case SqlDataType.Int
    31. .MySqlType = MySqlType.Int
    32. Case SqlDataType.Text
    33. .MySqlType = MySqlType.Text
    34. Case SqlDataType.VarChar
    35. .MySqlType = MySqlType.VarChar
    36. End Select
    37. End With
    38. Return parameter
    39. End Function
    40. Protected Overrides Function BekommeVerbindung() As DbConnection
    41. Return New MySqlConnection() With {
    42. .Host = Me.Server,
    43. .UserId = Me.Benutzer,
    44. .Password = Me.Passwort,
    45. .Database = Me.Datenbank,
    46. .Port = Me.Port,
    47. .Charset = "UTF8"}
    48. End Function
    49. End Class


    MsSqlVerbindung.vb:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Data.Common
    2. Imports System.Data.SqlClient
    3. <Serializable>
    4. Public Class MsSqlVerbindung
    5. Inherits SqlVerbindung
    6. Protected Overrides Sub SqlUpdate()
    7. Throw New NotImplementedException()
    8. End Sub
    9. Protected Overrides Function SqlSelect(Name As String, Optional Parameter() As DbParameter = Nothing) As DataTable
    10. Using con = BekommeVerbindung()
    11. Using cmd As New SqlCommand(AbfragenMS(Name), con)
    12. If Parameter IsNot Nothing Then
    13. cmd.Parameters.AddRange(Parameter)
    14. End If
    15. Using ada As New SqlDataAdapter(cmd)
    16. Using dt As New DataTable
    17. ada.Fill(dt)
    18. Return dt
    19. End Using
    20. End Using
    21. End Using
    22. End Using
    23. End Function
    24. Protected Overrides Function ErstelleParameter(Name As String, Value As Object, Typ As SqlDataType) As DbParameter
    25. Dim parameter As New SqlParameter
    26. With parameter
    27. .ParameterName = Name
    28. .Value = Value
    29. Select Case Typ
    30. Case SqlDataType.Int
    31. .SqlDbType = SqlDbType.Int
    32. Case SqlDataType.Text
    33. .SqlDbType = SqlDbType.Text
    34. Case SqlDataType.VarChar
    35. .SqlDbType = SqlDbType.VarChar
    36. End Select
    37. End With
    38. Return parameter
    39. End Function
    40. Protected Overrides Function BekommeVerbindung() As DbConnection
    41. Dim con As New SqlConnection
    42. If Me.IntegratedSecurity = True Then
    43. con.ConnectionString = New SqlConnectionStringBuilder With {
    44. .DataSource = Me.Server,
    45. .IntegratedSecurity = True,
    46. .InitialCatalog = Me.Datenbank}.ConnectionString
    47. Else
    48. con.ConnectionString = New SqlConnectionStringBuilder With {
    49. .DataSource = Me.Server,
    50. .UserID = Me.Benutzer,
    51. .Password = Me.Passwort,
    52. .InitialCatalog = Me.Datenbank}.ConnectionString
    53. End If
    54. Return con
    55. End Function
    56. End Class


    SqlFactory.vb:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class SqlFactory
    2. Public Shared Function Erstellen(ByVal Art As String) As SqlVerbindung
    3. Return If(Art = "MySQL",
    4. New MySqlVerbindung,
    5. New MsSqlVerbindung)
    6. End Function
    7. End Class


    Ich habe also in meiner Form eine BindingSource (DataSource = SqlVerbindung) und binde die Eigenschaften an meine Eingabemaske.
    Möchte ich jetzt eine Abfrage ausführen, so muss ich erst die DataSource in SqlVerbindung casten, dann nach der Art schauen und mir mittels der SqlFactory die entsprechende Klasse geben lassen. Dann habe ich die Einstellungen aber nicht mehr und müsste diese neu setzen. Ebenso, wenn ich die Einstellungen später lade um erneut eine Abfrage auszuführen, wie gehe ich da vor? Deserialisiere ich zur Klasse SqlVerbindung, schaue nach Art und gebe mir erneut mittels SqlFactory die korrekte Klasse wieder (ohne Einstellungen)? Das kann irgendwie nicht richtig sein. Ich renne wahrscheinlich gerade vom hundertsten ins tausendste ins millionste :thumbsup:

    Wie gehe ich das also am besten an?

    Über jede Antwort bin ich dankbar! :)

    *Topic verschoben*

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

    Hallo

    Warum ist "Art" ein String und kein Enumerator??

    Ich würde ein Interface machen welches die Methode beinhaltet und dann zwei Klassen (eine für MSsql und eine für Mysql) welche jeweils das Interface implementieren. So ist das sauber und dir ist später dann egal ws verwendet wird weil das Interface bestimmt was die Klassen "können".

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Also nehme erstelle ich mir eine Schnittstelle mit den Eigenschaften Server, Port, Benutzer, Passwort etc. Dann zwei Klassen, je für MySQL und MSSQL. Nehme die BindingSource und die DataSource = die Schnittstelle. Binde die Daten per Designer an die BindingSource.

    Möchte ich jetzt eine Abfrage machen, muss ich dennoch vorher noch schauen, ob Art = MySQL oder MSSQL (auch wenn es dann ein Enum ist), und entsprechend casten, oder nicht? Also quasi wie in meinen Startpost, nur mit einem Interface statt einer Basisklasse.

    Wie wird das denn in anderen Projekten geregelt?
    Na, so ganz hast du das mit dem Interface noch nicht raus.

    Ich kenne jetzt nicht so ganz die Unterschiede um dir ein genaueres Beispiel erstelen zu können.
    Würden denn die Abfragen selbst auch die gleichen sein oder musst du hier auch unterscheiden was jetzt "aktiv" ist. MS SQL oder MySQL.

    Denn ich denke einige abfragen werden anderst Formuliert werden müssen oder??

    Ich kann dir gerne ein Beispiel machen, diese Frage möchte ich aber geklärt haben, dann kann ich das ins Beispiel mit einfliessen lassen.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Es gibt Unterschiede bei den Abfragen, möchte ich zBsp. alle Datenbanken bekommen, ist es bei MySQL einfach SHOW DATABASES während es bei MSSQL SELECT name FROM master.dbo.sysdatabases ist.

    Aber auch bei anderen Abfragen, da fallen mir spontan schon die unterschiede ISNULL und IFNULL ein.

    Lösen würde ich das über eine Dictionary(Of String, String).

    Ich freue mich auf dein Beispiel, denn mit OOP und Interfaces etc. hab ich den Haken noch nicht raus! :)
    Hallo

    OK, ich habe jetzt mal die Struktur so erstellt wie ICH es angehen würde. Musst ja nicht genau so übernehmen, aber ich denke so ist es sehr flexibel.
    Ich führe in der kleinen Applikation jetzt kein Query ab. Ich habe hier gar keinen MySQL, nur einen MsSQL. Zum Testen habe ich jetzt im Interface definiert das ein String (das Query) zurückgegeben wird anstatt einem DataTable da ich die Abfrage nicht wirklich ausführe, ich will ja nur Testen ob richtig geschaltet wird je nachdem was für eine Serverart gewählt wurde.

    PS: Kann sein das du mal von grund auf Kompilieren oder sogar "NuGet Packete wiederherstellen" musst. Ich habe ein NuGet PAket eingebunden um MySQL einbinden zu können da ich ja keinen habe.

    Anbei auch ein Diagram.
    Viel spass damit.

    Grüße
    Sascha
    Bilder
    • ClassDiagram1.png

      47,85 kB, 1.267×737, 107 mal angesehen
    Dateien
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    KingLM97 schrieb:

    Gegeben ist [...]
    Ähm - und was ist gesucht?
    Also dein Problem im allgemeinen finde ich nicht beschrieben.
    Du schreibst zwar: "Möchte ich jetzt eine Abfrage ausführen, so muss ich erst die DataSource in SqlVerbindung casten, dann nach der Art schauen und mir mittels der SqlFactory die entsprechende Klasse geben lassen."
    Aber vlt musst du das alles garnet.
    Ich hab jetzt dein Code, wo du das alles musst, nicht durchgeguckt, aber ich könnte mir vorstellen, wenn du die gemeinsame Basisklasse von MySqlAdapter, SqlClientAdapter verwendest, dann braucht dein Code ühaupt nicht zu wissen, ob er MySql abruft oder SqlServer.
    Jdfs. allgemeine Zugriffs-Lösung für: MySql, Access, SqlCe, SqlServer, DatasetOnly habich nach diesem Prinzip designed.

    Aber vlt. ist dein Problem ja ein anderes - wie gesagt: für eine vollständige Aufgabenbeschreibung fehlt der "Gesucht ist..." - Abschnitt.
    @Nofear23m

    Werde ich mir die Tage mal anschauen, schonmal danke dafür!

    @ErfinderDesRades

    Gesucht ist eine Lösung, wie ich einfach beides unterstützen kann.
    In meinen alten Projekten habe ich mir einfach eine Art globale Variable gesetzt und diese mit "MySQL" oder "MSSQL" gefüllt. Das sah dann ungefähr so aus:

    VB.NET-Quellcode

    1. Private Sub DoSomething()
    2. Dim dt as DataTable = Nothing
    3. If GlobaleVariablen.SqlArt = "MySQL"
    4. Dim sql As New MySQL
    5. dt = sql.SqlSelect("SHOW DATABASES")
    6. Else
    7. Dim sql As New MsSQL
    8. dt = sql.SqlSelect("SELECT name FROM master.dbo.sysdatabases")
    9. End If
    10. 'irgendwas mit dt machen...
    11. End Sub


    Innerhalb der jeweiligen Klasse habe ich dann die Einstellungen noch geladen und gesetzt. Das finde ich sehr unschön. Das muss auch irgendwie anders funktionieren. Mein erster Versuch war der Ansatz mit der Basisklasse.
    Dein Projekt habe ich mir mal gezogen, aber um die Uhrzeit werde ich da nicht so schnell durchsteigen können ?(
    Eine kleine Erklärung dazu wäre sehr hilfreich :)
    kleine Erläuterung ist gut. Ich kann jetzt hier kaum das Prinzip Vererbung, Baisklasse und so mal eben "klein erläutern".

    Ich kann dir nur sagen: Verwende die gemeinsame Basisklasse der MySql- und SqlClient - Objekte, dann brauchst du deinen Code nur einmal zu schreiben.

    Beispiel DataAdapter: Da hat MySql den MySqlDataAdapter, und SqlClient den SqlDataAdapter. Dassis natürlich nervig, da immer zu gugge, was mussich nu für einen nehmen.

    Aber die gute Nachricht ist: MySqlDataAdapter und SqlDataAdapter haben eine gemeinsame Basisklasse, den DbDataAdapter.
    Dann nimm den, dann liegst du in jedem Falle richtig.

    Dasselbe gilt auch für alle anderen Datenzugriffs-Objekte: Connection, Command, CommandBuilder,...
    Die Idee dahinter ist ja, dass die Klassen die zur Datenbankverbindung dienen möglichst vollständig die Logik dafür beinhalten. D.h. solche Sachen wie "bin ich MySQL oder MSSQL?" sollten erst gar nicht ausserhalb der Datenbanklogik auftauchen.

    Sowas wie "GetAllTables()" könnte dann zB eine abstrakte Methode der Klasse SqlVerbindung sein. Somit müssten deine MSSQL und MySQL Klassen diese implementieren (beide implementieren es anders, und zwar der eine macht die SQL Anfrage mit "SHOW DATABASES" und der andere es halt anders) und wups hast du das ganze vor deinem restlichen Code "versteckt". Wenn du jetzt ein Objekt mit deiner Factory Klasse machst kannst du immer auf GetAllTables() zugreifen ohne wissen zu müssen ob dahinter jetzt MSSQL oder MySQL steht.