Code zur Laufzeit in eine Modul einladen; evtl. aus Datenbank

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

Es gibt 38 Antworten in diesem Thema. Der letzte Beitrag () ist von DrZwockel.

    Code zur Laufzeit in eine Modul einladen; evtl. aus Datenbank

    Hallo VB-Gemeinde,

    ich habe ein Projekt, in dem es ein Modul gibt, das mit einer If-Schleife durchlaufen wird. Zurückgemeldet wird bei einem Treffer ein SQL-String, der dann an eine DB gesendet wird. Dieser Code steht in einem Modul und ist Bestandteil der komilierten DLL (Office-AddIn).

    Leider ist diese Sache recht statisch, da ich jedes mal, wenn sich ein Parameter verändert oder hinzukommt, der einen SQL-String als Ergebnis benötigt, dieses Modul bearbeiten und dann die DLL neu erstellen muss. Leider muss das Paket dann firmenweit wieder installiert werden.

    Jetzt meine Frage: Habe ich eine Möglichkeit, ein Modul zur Laufzeit mit Text zu füllen, z.B. aus einer Datenbank, der dann der Code ist, der mit der If-Schleife durchlaufen wird?

    Vielen Dank für Eure Hilfe


    Gruß vom Doc
    prinzipiell kann man FreiText zur Laufzeit kompilieren und ausführen - Stichwort CodeDomCompiler.
    Der Benutzer muss aber ein guter Programmierer sein, weil da gibts keine Intellisense, Background-Compiler und sowas - also höchst instabil sowas.
    Nächstes Problem ist Sicherheit: Ein Angreifer kann da eben wirklich beliebigen Code ausführen.

    Ich fänds günstiger, du würdest ein Datenmodell finden, was deine Problematik abbildet.
    Dann gibt der User Daten ein, und das Proggi verarbeitet die - das ist sowohl einfacher als auch sicherer.
    Hallo ErfinderDesRades,

    mein Problem sieht so aus: Der User Wählt eine Tabelle aus, diese schreibt das Programm mit einer Parametrisierung in eine Excelzeile. Zum Datenholen wird diese Excelzeile durchlaufen und der Name der Tabelle abgefragt. Dieser Name wird dann in dem Code (If-Schleife) gesucht und der der entsprechende SQl-String dann mit den Parametern gefüllt und dann an die Datenbank gesendet, um die Daten zu laden (Monatswerte).

    Ich müsste es also schaffen, diesen Abgleich und das sind gute 1000 Zeilen, da es soviele Tabellenmöglilchkeiten sind, zu durchlaufen.
    Ich hoffe, dass es einigermaßen verständlich ...

    Gruß vom Doc
    @EaranMaleasi Jou, das sehe ich auch so.

    DrZwockel schrieb:

    jedes mal, wenn sich ein Parameter verändert oder hinzukommt,
    Wie sieht es denn aus, wenn Du per String.Format() einen variablen String generierst, der dann verwendet wird?
    Der lässt sich dynamisch testen und die vom @ErfinderDesRades genannten Probleme und Gefahren treten da nicht auf. :D
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Guten Morgen,

    ich versuche es noch einmal zu erklären:

    In einer Excezeile werden in verschiedenen Zellen diverse Informationen abgelegt. U.a. der Name der Tabelle und eine weitere Parametriesierung. Daraus wird dann ein SQL-String für eine Datenbankabfrage zusammengesetzt.
    Da aber jede Tabelle u.a. ein anderes Wertfeld hat oder eine Sub-Select erforderlich ist, wird der Tabellenname durch eine If-Schleife "geschickt" und der passende "Körper" für den Zusammenbau gesucht. Diese "Körper" liegen in einer Klasse, die die verschiedenesten Modelle zu den diversen Tabellen beinhaltet. Ist der richtige "Körper" gefunden, wird der SQL-String komplettiert und an die Datenbank gesandt.

    Hier mal eine Ausschnitt des Codes, der die If-Struktur beinhaltet:

    VB.NET-Quellcode

    1. Public Function AbfrageSyntaxErkennen(ByVal TABELLENOWNER As String, ByVal PERIODE As String, ByVal WERTFELD As String, ByVal BEDINGUNG As String, JAHR As String) As String
    2. Dim SQL_String As New StringBuilder
    3. #Region "TEST"
    4. If UCase(TABELLENOWNER) = "TEST.TEST1" Or UCase(TABELLENOWNER) = "TEST.TEST2" Or UCase(TABELLENOWNER) = "TEST.TEST3" Then
    5. '### TEST Tabellen
    6. SQL_String.Clear()
    7. SQL_String.Append("SELECT ")
    8. SQL_String.Append("" & PERIODE & ",")
    9. SQL_String.Append(" SUM(")
    10. SQL_String.Append("" & WERTFELD & "")
    11. SQL_String.Append(") AS PWERT ")
    12. SQL_String.Append(" FROM ")
    13. SQL_String.Append("" & TABELLENOWNER & "")
    14. SQL_String.Append(" WHERE ")
    15. SQL_String.Append("" & BEDINGUNG & "")
    16. SQL_String.Append(" AND YYYYMM BETWEEN ")
    17. SQL_String.Append("" & JAHR & "")
    18. SQL_String.Append("01 ")
    19. SQL_String.Append("AND ")
    20. SQL_String.Append("" & JAHR & "")
    21. SQL_String.Append("12 ")
    22. SQL_String.Append(" GROUP BY YYYYMM ")
    23. SQL_String.Append(" ORDER BY YYYYMM")
    24. Return SQL_String.ToString
    25. End If
    26. ...


    Zurück kommt dann der fertige "Körper".

    Mein Problem besteht darin, dass die Tabellenauswahl zentral über eine Datenbanktabelle in die Appliktion gegeben werden, somit sehr dynamisch. Aber der Code zur Erkennung der korrekten Syntax (s. Code). ist statisch in der DLL und muss beim Vorliegen einer neuen Tabelle oder Änderung einer Bezeichnung manuell angepasst und neu kompiliert ... und dann wieder firmenweit installiert werden.

    Gibt es hierfür überhaupt eine praktiable Lösung ... :S ?(


    Vielen Dank,

    Gruß vom Doc

    Vollzitat entfernt. ~Thunderbolt

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

    @DrZwockel Dann ist es besser, Du machst ein Plugin-System, wo neue Syntaxe drin compiliert werden.
    Das Programm lädt alle DLLs des entsprechenden Plugin-Systems und füttert sie der Reihe nach, bis der Output stimmt.
    Wurde keine Übereinstimmung gefunden, musst Du eine neue DLL machen.
    Feddich. :D
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo RodFromGermany,

    vielen Dank für Deine Antwort, allerings kann ich mit der Begriffllichkeit "Plugin-System" nichts anfangen, da mir in diesem Bereich wohl die Erfahrung bzw. die Kenntnis fehlt. Wie kann ich mir so etwas vorstellen?


    Gruß vom Doc

    Vollzitat entfernt. ~Thunderbolt

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

    DrZwockel schrieb:

    allerings kann ich mit der Begriffllichkeit "Plugin-System" nichts anfangen
    Dafür gibt es die Suchfunktion:
    Wie erstelle ich ein PlugIn System unter VB.NET (Framework 2)
    Erstellen eines Plugin-Systems mit VB.NET und MEF ab .NET 3.5
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo RodFrom Germany.

    vielen Dank füt die Links ... ich habe mir das mal geschaut und werde die Variante mit dem MEF propieren ...

    Ich werde dann nachberichten :thumbup:

    Gruß vom Doc



    Guten Abend RodFromGermany,

    vielen Dank nochmals für Deine Hilfe und für den Richtungszeig auf den Lösungsweg. Ich habe das Problem mit der zweiten Plugin-System Variante (MEF) lösen können. Es dauerte zwar bis ich verstanden habe, was da passiert, aber die Arbeit scheint sich gelohnt zu haben.
    Zuhause habe ich die Lösung in einem kleinen Testmodul ausprobiert und es funktionierte.

    Jetzt werde ich es morgen im Betrieb umsetzen und die Inhalte der If-Schleife in das Plugin setzen.
    Was wirklich genial ist, dass es egal ist, wo dass Plugin liegt. Ich habe es mal Testweise auf einen anderen Bereich des Servers gelegt und die Verbindung klappte super (wenn der Pfad stimmt 8o ).

    Ob die große Lösung auch so reibungslos funktioniert hat, werde ich morgen dann nachberichten.

    Gruß vom Doc

    Beiträge zusammengefügt. Vollzitat entfernt. ~Thunderbolt

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

    DrZwockel schrieb:

    If-Schleife

    Dir ist klar, dass du dich mit diesem Wort jedes mal komplett blamierst?
    -> if-schleife.de/
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    @DrZwockel Das geniale an diesem System ist, dass Du mit Interfaces ohne Inhalt ( :!: ) Zuordnungen und Sortierungen von Plugin-DLLs vornehmen kannst ohne eine einzige zusätzliche Zeile Code.
    Es genügt, dass eine DLL-Klasse ein (leeres) Interface implementiert, um sie als solche zu erkennen. 8o
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Guten Morgen mrMo,

    vielen Dank für den Tipp ... ich lerne jeden Tag dazu und muss feststellen, dass ich im VB-Universum ein kleines Staubkorn bin ^^ . Ich werde zukünftig nur noch von If-Abfragen sprechen ...

    Gruß vom Doc

    Vollzitat entfernt. ~Thunderbolt

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

    Hallo,

    der Lösungsansatz im Kleinen hatte funktioniert, im Betrieb ist es voll in die Hose gegangen.

    Solange man nur eine Variable übergibt und die Antwort direkt in der DLL hervorruft (z.B. Ausgabe in Messagebox oder auf der Console) ist alles ok.

    In meiner Lösung muss ich 6 Parameter (Typ String) übergeben und dann ein Ergebnis zurückgeliefert bekommen, wo die 6 Parameter in einen String eingebaut sind und so einen vollständigen SQL-String ergeben. Dieser SQL-String wird dann im Hauptprogramm zum Absetzen der Abfrage an den DB-Server verwendet.

    Vielleicht geht das ja zum einen mit mehreren Parametern und einer Rücklieferung an den Aufrufenden (-> als String), aber ich bekommen das nicht hin ... Ende des Horizontes scheint erreicht zu sein ;( .

    Jemand so etwas schon mal gemacht?
    Dank an alle, die geholfen haben und weiterhin helfen ...

    Gruß vom Doc

    DrZwockel schrieb:

    im Betrieb ist es voll in die Hose gegangen.
    Wie äußert sich das?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo RodFrom Germany,

    dass er beim Durchlaufen der Ladesequenz und dem Zuordnen in den Container

    VB.NET-Quellcode

    1. Dim Container = new CompositionContainer(New DirectoryCatalog("./Plugin"))
    2. Container.ComposeParts(ME)


    einen Fehler wirft:

    "... Mindestens ein Typ in der Assembly kann nicht geladen werden. Rufen Sie die LoaderExcepions-Eigenschaft ab, wenn Sie weitere Informationen benöigen."
    Die Anzahl der Parameter, die übergeben werden, stimmen im Interface und in der PluginDatei überein.
    Ach ja, der Inhalt entspricht dem Inhalt aus dem Post #6.
    Wie gesagt, die kleine Lösung (angelegt an das Beispiel hier aus dem Forum) lief, ich habe jetzt nur Parameter zur Übergabe rein gepackt ..

    Gruß vom Doc
    @DrZwockel Kommen in dieser DLL weitere Klassen / Interface vor?
    Kannst Du mal ein kleines Testprojekt machen, das den Effekt reproduziert?
    Wenn ja: ohne Binaries zippen und anhängen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo RodFromGermany,

    vielen Dank für Dein Hilfsangebot.

    Also zu Deiner Frage: Nein, es kommt nur eine Klasse und auch nur ein Interface in dieser DLL vor.

    Wie bereits beschrieben, in einem Musterbeispiel, dass hier als MEF vorgestellt wurde, funktioniert alles einwandfrei, wobei und das ist schon sehr störend ist, dass es nur ein Boolean zurückliefert. Sicherlich sendet das Modul dieen Boolean zurück, allerdings ist es nicht das, was ich benötige.

    Ich skizziere noch einmal kurz die Problemstellung:
    1. Einzelne Variablen in einer Exceltabelle
    -> Die Zeile wird ausgelesen und der Inhalt der Zellen an die Variablen übergeben
    2. Die Sammlung der Variablen werden an eine Funktion übergeben.
    -> Abgleich nach einem Tabellennamen. Ist dieser gefunden, so werden die Variablen in einem Abschnitt der If-Abfrage eingesetzt, woraus ein kompletter SQL-String zur Abfrage einer Datenbank entsteht.
    3. Die Funktion sendet den fertigen SQL-String an die aufrufende Funktion zurück.
    -> Der String wird geprüft und dann an die Datenbank gesandt, die dann das Ergebnis zurücksendet, welches dann in Excel eingetragen wird.
    4. Danach wird die nächste Zeile durchlaufen.
    -> Der Schritt 1 beginnt wieder von vorn, bis alle Zeilen, die entsprehende Infos haben, durchlaufen sind.

    So sieht die Funktion aus, in der alles beginnt ... (auszugsweise):

    Modul, welches die Anfrage sendet und das Ergebnis zur Weiterarbeit benötigt:

    VB.NET-Quellcode

    1. Dim SQL_Ergebnis As String = FLS.AbfrageSyntaxErkennen(UCase(OTabelle), UCase(OPeriode), UCase(OWertfeld), UCase(OBedingung), UCase("JAHRAUSWERTUNG"))
    2. If SQL_Ergebnis = "" Then
    3. MessageBox.Show("Es wurde kein SQL-String in die Datenbank gespeichert. Das Ergebnis war leer", "SQL-Fehler bei Neuanlage", MessageBoxButtons.OK, MessageBoxIcon.Error)
    4. Exit Sub
    5. Else
    6. SQLCMD.Parameters.Add("P_SQL_STRING", SQL_Ergebnis)
    7. '
    8. ws.Cells(I, CByte(My.Resources.TSQL)).value = "OSQL"
    9. End If


    Die Anfrage an: FLS.AbfrageSyntaxErkennen(...) geht hier hin:

    VB.NET-Quellcode

    1. Public Class FL_SQL_QUERIES
    2. #Region "SQL-Abfrage Syntax"
    3. ''' <summary>
    4. ''' YAB Tabellen
    5. ''' </summary>
    6. ''' <param name="TabellenOwner"></param>
    7. ''' <param name="Periode"></param>
    8. ''' <param name="Wertfeld"></param>
    9. ''' <param name="Bedingung"></param>
    10. ''' <param name="Jahr"></param>
    11. ''' <returns></returns>
    12. Public Function AbfrageSyntaxErkennen(ByVal TABELLENOWNER As String, ByVal PERIODE As String, ByVal WERTFELD As String, ByVal BEDINGUNG As String, JAHR As String) As String
    13. Dim SQL_String As New StringBuilder
    14. #Region "YABDATEN"
    15. If UCase(TABELLENOWNER) = "XX.YYYYYYYY" Or UCase(TABELLENOWNER) = "XX.AAAA" Or UCase(TABELLENOWNER) = "XX.BBBB" Then
    16. '### YAB Tabellen
    17. SQL_String.Clear()
    18. SQL_String.Append("SELECT ")
    19. SQL_String.Append("" & PERIODE & ",")
    20. SQL_String.Append(" SUM(")
    21. SQL_String.Append("" & WERTFELD & "")
    22. SQL_String.Append(") AS PWERT ")
    23. SQL_String.Append(" FROM ")
    24. SQL_String.Append("" & TABELLENOWNER & "")
    25. SQL_String.Append(" WHERE ")
    26. SQL_String.Append("" & BEDINGUNG & "")
    27. SQL_String.Append(" AND YYYYMM BETWEEN ")
    28. SQL_String.Append("" & JAHR & "")
    29. SQL_String.Append("01 ")
    30. SQL_String.Append("AND ")
    31. SQL_String.Append("" & JAHR & "")
    32. SQL_String.Append("12 ")
    33. SQL_String.Append(" GROUP BY YYYYMM ")
    34. SQL_String.Append(" ORDER BY YYYYMM")
    35. Return SQL_String.ToString
    36. End If
    37. #End Region
    38. ...


    In dieser Klasse folgen noch jede Menge weitere Tabellen, die alle nach dem gleichen Prinzip aufgebaut sind.

    So, nun zu den Arbeiten für das MEF:

    Hier mal der Code aus der Klasse Pluginverbindung:

    VB.NET-Quellcode

    1. Imports System.ComponentModel.Composition
    2. Imports System.ComponentModel.Composition.Hosting
    3. Public Class Pluginverbindung
    4. <ImportMany(GetType(IPlugin))> Public Plugins As IPlugin()
    5. Public Sub LadePlugins()
    6. Dim catalog = New DirectoryCatalog("C:\Daten\Plugin\")
    7. Dim container = New CompositionContainer(catalog)
    8. container.ComposeParts(Me)
    9. End Sub
    10. Public Interface IPlugin
    11. Function SQL_String1(ByVal TABELLENOWNER As String, ByVal PERIODE As String, ByVal WERTFELD As String, ByVal BEDINGUNG As String, JAHR As String) As Boolean
    12. End Interface
    13. End Class



    Die "externe Plugin DLL" sieht so aus:

    VB.NET-Quellcode

    1. Imports System.ComponentModel.Composition
    2. Imports FX.Pluginverbindung
    3. Imports System.Text
    4. Public Class Queries
    5. '### Was soll das Plugin machen
    6. <Export(GetType(IPlugin))>
    7. Public Class SQLStringPlugin
    8. Implements IPlugin
    9. Public Function SQL_String1(ByVal TABELLENOWNER As String, ByVal PERIODE As String, ByVal WERTFELD As String, ByVal BEDINGUNG As String, JAHR As String) As Boolean Implements IPlugin.SQL_String1
    10. Dim SQL_String As New StringBuilder
    11. If UCase(TABELLENOWNER) = "XX.YYYYY" Or UCase(TABELLENOWNER) = "XX.AAAA" Or UCase(TABELLENOWNER) = "XX.BBBB" Then
    12. '### YAB Tabellen
    13. SQL_String.Clear()
    14. SQL_String.Append("SELECT ")
    15. SQL_String.Append("" & PERIODE & ",")
    16. SQL_String.Append(" SUM(")
    17. SQL_String.Append("" & WERTFELD & "")
    18. SQL_String.Append(") AS PWERT ")
    19. SQL_String.Append(" FROM ")
    20. SQL_String.Append("" & TABELLENOWNER & "")
    21. SQL_String.Append(" WHERE ")
    22. SQL_String.Append("" & BEDINGUNG & "")
    23. SQL_String.Append(" AND YYYYMM BETWEEN ")
    24. SQL_String.Append("" & JAHR & "")
    25. SQL_String.Append("01 ")
    26. SQL_String.Append("AND ")
    27. SQL_String.Append("" & JAHR & "")
    28. SQL_String.Append("12 ")
    29. SQL_String.Append(" GROUP BY YYYYMM ")
    30. SQL_String.Append(" ORDER BY YYYYMM")
    31. Return SQL_String.ToString
    32. End If
    33. End Function
    34. End Class
    35. End Class


    Der Abschnitt, der den Aufruf übernimmt sieht so aus:

    VB.NET-Quellcode

    1. ...
    2. Dim verbindung as new Pluginverbindung()
    3. verbindung.LadePlugins()
    4. For Each plugin as IPlugin In verbindung.Plugins
    5. plugin.SQL_String1(SQL_String)
    6. next
    7. ...


    Der Fehler wird dann an dieser Stelle geworfen:

    VB.NET-Quellcode

    1. container.ComposeParts(Me)


    "... Mindestens ein Typ in der Assembly kann nicht geladen werden. Rufen Sie die LoaderExcepions-Eigenschaft ab, wenn Sie weitere Informationen benöigen."

    Irgendwie scheint hier etwas verdreht oder falsch zu sein, nur finde bzw. sehe ich es nicht ;( .


    Gruß vom Doc

    DrZwockel schrieb:

    nur finde bzw. sehe ich es nicht
    In Deinem Code finde und sehe ich auch nichts, dazu wäre das compilierbare Projekt von Nöten, das ich nicht habe.
    Mach ein separates Rumpf-Projekt auf Basis dieses Projekts und schmeiß allen direkten DB-Zugriff raus, weil es daran nicht liegen sollte.
    Überzeuge Dich, dass Dein Effekt weiterhin auftritt.
    wenn Du das Projekt "minimiert" hast, zippe es ohne Binaries und häng es an einen Post.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!