Aus Nebenthread in Dataset lesen

  • VB.NET
  • .NET (FX) 4.0

Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von cl10k.

    Aus Nebenthread in Dataset lesen

    Hallo,


    ich nutze ein Dataset um (u.a.) Anwendungseinstellungen zu speichern.

    Über kleine Funktionen lese ich Infos aus, die ich gerade benötige, z.B. die derzeit eingestellte Sprache:

    VB.NET-Quellcode

    1. Public Function GetLanguage() As String
    2. Return CType(FIRST_DataSet.dt_Settings.Rows(0).Item("Language"), String)
    3. End Function


    Das klappt auch prima. Rufe ich die Funktion allerdings aus einen Nebenthread auf, gibt es eine Fehlermeldung:


    A first chance exception of type 'System.IndexOutOfRangeException' occurred in System.Data.dll

    Additional information: An der Position 0 befindet sich keine Zeile.


    Der gesuchte Eintrag ist zu diesem Zeitpunkt aber garantiert vorhanden (getestet!). Mein Verdacht ist nun der Nebenthread. Nur wie komme ich von dort an das DataSet heran?

    Ich habe mir gestern schon ein wenig zu Delegates und Invokes angeschaut und kann damit aber bisher nur primitive Sachen umsetzen (z.B. aus einem Nebenthread in ein Label schreiben). Aber für obiges Problem reicht mein Verständnis noch nicht aus.

    (Anmerkung: Ich rufe diese Funktion nur aus dem Nebenthread auf, wenn dort irgendwas schiefgelaufen ist und ich eine lokalisierte Version der Hilfe aufrufen möchte, dazu brauche ich aber die eingestellte Sprache. Hier ggf. Invokes zu nutzen, sollte dementsprechend kein Problem darstellen, weil das Kind eh schon in den Brunnen gefallen ist.)

    lg Christian
    Wie genau rufst du denn GetLanguage auf?
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Meine Anwendung ist ziemlich umfangreich, ich breche es darum mal auf das Wesentliche herunter:

    Ich befinde mich grundsätzlich in einem BackgroundWorker. Wird dort ein Try-Catch-Fehler gefangen, rufe ich mittels Using-Statement eine frische Instanz eines kleinen Forms mit Infos auf. Über einen Button auf diesem Form führt :

    VB.NET-Quellcode

    1. Dim Test As String = scls_LanguageManager.GetLanguage 'Die Funktion liegt in einer statischen Hilfsklasse


    dann zu o.g. Fehler.

    Ich weiß nicht worauf du hinauswillst, brauchst du es genauer?

    lg
    Hi.

    aus statischen Klassen sollte man niemals auf instanzierte Objekte zugreifen.
    Mich wundert es, dass es überhaupt funktioniert. Vermutlich greifst aber iwie auf eine andere Instanz zu, als in deiner Hauptanwendung und daher ist das Dataset auch nicht befüllt.
    Das ist meine Signatur und sie wird wunderbar sein!

    Mono schrieb:

    Hi.
    aus statischen Klassen sollte man niemals auf instanzierte Objekte zugreifen.


    Was spricht dagegen? Die statische Klasse ist einfach eine Methodensammlung parallel zu meinem Hauptform. Meine Anwendung umfasst ca. 14.000 LOC. Ich kann den ganzen Code ja nicht im Hauptform rumfliegen lassen.

    Mono schrieb:

    Hi.
    Mich wundert es, dass es überhaupt funktioniert. Vermutlich greifst aber iwie auf eine andere Instanz zu, als in deiner Hauptanwendung und daher ist das Dataset auch nicht befüllt.

    Den Satz kann ich nicht nachvollziehen (von wo nach wo...). Generell funktioniert es hier problemlos aus meiner statischen Hilfsklasse auf das DataSet zuzugreifen. Es existiert auch nur eine Instanz des DataSets.
    Du redest von einem Modul nehm ich an? Ein Modul hat keine Instanz. Und in einer statischen Methode aus einem Modul kannst du nicht einfach auf iwelche Instanzen zugreifen, ohne Sie explizit der Methode zu übergeben. Denn woher soll die statische Methode die Instanz kennen ? Sie kann maximal auf andere statische Methoden zugreifen.
    Das ist meine Signatur und sie wird wunderbar sein!
    Generell instanziere ich meinen Kram ganz normal in frm_Main und beachte auch die übliche Vorgehensweise zum Informationstransport in der Klassenhierarchie (nicht direkt auf Parents zugreifen, etc).

    Meine Helferklassen sind keine klassischen Module, sondern "Public NonInheritable Classes" mit leerem Konstruktor. In der Objekthierarchie stehen die parallel zu frm_Main, entsprechend greife ich auch ganz stur aus meinen statischen Methoden auf das DataSet über "frm_Main.FIRST_DataSet" zu.

    Wie gesagt, ausser aus dem BGW funktioniert das gut.
    Offenbar weißt du gar nicht was statisch bedeutet. Eine Notinheritable Klasse ist keine statische Klasse. Eine statische Klasse ist in vb.net ein Modul.
    Eine Funktion:

    VB.NET-Quellcode

    1. Public Function GetLanguage() As String

    ist auch nicht statisch sondern eine ganz normale Methode für eine Instanz. Nur wenn der Konstruktor leer ist heißt es nicht, dass die Klasse nicht instanziert wird, wenn du daraus ein Objekt mit "New" erstelltst.
    Eine statische Methode hätte das Schlüsselwort "Shared" in vb.net.

    Generell instanziere ich meinen Kram ganz normal in frm_Main und beachte auch diese Klassenhierarchie

    Also erzeugst du da eine Instanz der Hilfsklasse?

    Was erledigst du denn btw im Backgroundworker? Ich würde die nämlich grundsätzlich davon abraten..
    Das ist meine Signatur und sie wird wunderbar sein!
    Tatsächlich ist GetLanguage auch eine statische Funktion. Ich habe shared im Eingangspost nur weggelassen, weil ich den Umstand hier aus einer statischen Klasse heraus zu arbeiten, für nicht wichtig erachtete und die Diskussion nicht mit solchen Details vom eigentlichen Problem wegführen wollte.

    Gem. Stackoverflow ist es wurscht ob man statische Methodensammlungen in Module stopft oder eben in NonInheritable Classes. Der Vorteil ist halt, dass letztere quasi ihren eigenen Namespace mitbringen. Ansonsten ist die Verwendung vollkommen äquivalent, sie werden also auch nicht instanziert. (Ich bin aber auch nur ein blutiger Amateur und die Programmstruktur ist vor Jahren entstanden. Damals habe ich mir dabei was gedacht, heute ich kann ich mich nicht mehr richtig erinnern.) Ich verstehe gut, worauf du hinaus willst, ich glaube aber das es hier im Kontext irrelevant ist (ich mag mich aber auch irren) :)
    ----
    Im BGW greife ich auf ein CAD-Programm zu. Das ist sehr zeitintensiv und würde die Hauptanwendung komplett lahmlegen. Generell nutze ich die BGW.ProgressChanged um Informationen zurück in den Hauptthread zu transportieren. Nur wenn im BGW mal etwas schief läuft, würde ich halt gern direkt aus dem DoWork heraus auf diese spezielle GetLanguage-Funktion des Hauptthreads zugreifen...

    cl10k schrieb:


    Gem. Stackoverflow ist es wurscht ob man statische Methodensammlungen in Module stopft oder eben in NonInheritable Classes. Der Vorteil ist halt, dass letztere quasi ihren eigenen Namespace mitbringen. Ansonsten ist die Verwendung vollkommen äquivalent, sie werden also auch nicht instanziert. .


    NotInheriable Classes müssen instanziert werden. Shared Funktionen benötigen natürlich keine Instanz. Aber ich hacke da nicht ohne Grund drauf rum. VS für VB.NET erlaubt nämlich genau das was du tust. Das ist aber eigentlich scheiße, denn für jeden THREAD wird dir so eine "Standard" Shared Instanz für deine frmMain zurückgegeben. Dies ist auch der Grund, warum du im Nebenthread eine andere "Instanz" hast, als in deinem Hauptthread. Da du mit der dieser komischen aus VB4/5/6 Zeiten übernommenen Art auf die Form zugreifen zu können arbeitest.

    Daher solltest du an diesem Grunddesignproblem arbeiten ;)

    //Edit:
    Ich habe hier nur C# und verwende fast nur noch C#, und da ist dies von Haus aus gar nicht möglich. Ich müsste meine Form Klasse dazu erweitern Siehe hier:
    http://stackoverflow.com/questions/4698538/why-is-there-is-a-default-instance-of-every-form-in-vb-net-but-not-in-c

    //edit2:
    Das ThreadStatic Attribut macht das ganze dann einzigartig pro Thread
    Das ist meine Signatur und sie wird wunderbar sein!

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

    Das hat nix mit dem Modul zu tun. Ein Modul voller shared Methoden ist das gleiche Problem. Das Problem ist das du auf die Standard Shared Instanz der Form zugreifst!

    Es geht mir eigentlich um das Grundverständnis. Ich habe nich gewusst das du das "Shared" bei deiner Function weggelassen hast (Was ja eine WICHTIGE Information ist). Und dann trifft das wieder zu, was ich in meinem ersten Post geschrieben habe. Ich wundere mich das es geht, da es in c# nicht gehen würde, und dann fiel mir ein, dass vb ja die ganzen vb6 Leute mit an Board nehmen wollte... und diese komische default shared Instanz dabei ist!
    Das ist meine Signatur und sie wird wunderbar sein!

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

    Genau das begreife ich nicht. Was ist/bedeutet "die Standard Shared Instanz der Form"?

    Das DataSet wird in meiner Hauptanwendung (frm_Main) als FIRST_DataSet instanziert.

    Es stellt bislang keine Schwierigkeit dar, aus einer (pseudo) statischen Hilfsklasse auf frm_Main.FIRST_DataSet zuzugreifen. Das sollte doch einen eindeutigen Verweis auf genau die richtige Instanz des DataSets darstellen. Wie kommt da diese obskure "Standard Shared Instanz der Form" (von frm_Main?) dazwischen? Und wieso zickt das Vorgehen nur aus dem BGW?

    Ich glaube dir gern, dass du dem Problem auf der Spur bist und hoffe auf deine Geduld. Aber ich blicke es kein bisschen... :cursing:

    EDIT: Habe gerade dein Edit gelesen. Jetzt ist es klarer. Ich werde trotzdem mal auf deine nächste Antwort warten, damit die Reihenfolge wieder ins Lot kommt...

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

    Hast du den Link gelesen den ich geschickt habe?

    Why is there is a default instance of every form in VB.Net but not in C#?

    In einer Shared Methode kann man nicht auf eine Instanz!! zugreifen. Nur auf Shared Members.

    Ignorieren wir mal den aktuellen Fall und denken uns 2 Klassen. Eine ist eine normale Klasse und die anderen von mir aus NotInheritable mit einer statischen Methode (VB.NET -> Shared)

    VB.NET-Quellcode

    1. Public Class NormalClass
    2. Public Sub New()
    3. End Sub
    4. Public Member as String
    5. End Class
    6. '
    7. Public NoInheritable Class Helper
    8. Public Shared Function GetStringInFrench() as String
    9. Return Translate(NormalClass.Member) 'geht nicht
    10. End
    11. Private Function Translate(What as String) as String
    12. '..
    13. End Function
    14. End Class


    Das geht nicht. Die Shared Function kann nicht auf NormalClass.Member zugreifen. Denn Member ist nicht statisch, sondern gehört zur Instanz von NormalClass. Genauso greifst du aber auf das Dataset zu. Und das geht nur, da es ein Shared frmMain Member gibt.

    Dazu kommt, dass in VB.NET die Member (also Controls) auch noch als Friend deklariert sind und nicht private. Daher kannst du innerhalb der Assembly auch drauf zugreifen.

    Aber du greifst halt nicht auf die tatsächliche Instanz zu, sondern schreibst einfach
    frmMain.

    frmMain ist im Grunde ein statischer Member von frmMain der im Normalfall die erzeugte Instanz wiedergibt.

    Intern passiert dadurch im Grunde das was im Link beschrieben wird. Das geht aber eben nur gut, solange man sich im selben Thread bewegt. Andernfalls erhalt man eine NEUE Instanz der Form.

    //Edit: paar Schreibfehler ausgebessert.

    Ich weiß nicht mehr wie ich es noch besser erklären kann. Falls es immer noch nicht klar ist, dann empfehle ich dir dringend dich ein wenig mit Statisch vs InstanzenMembern zu belesen.

    LG
    Das ist meine Signatur und sie wird wunderbar sein!

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

    cl10k schrieb:

    Es stellt bislang keine Schwierigkeit dar, aus einer (pseudo) statischen Hilfsklasse auf frm_Main.FIRST_DataSet zuzugreifen.
    Na, dieser Thread beweist doch wohl deutlich das Gegenteil: Seit 4h kommst du in diesem Punkt nicht weiter.

    Ganz besonders Threading und die Shared Default-Instanz funktioniert gar nicht. Denn der Thread erstellt seine eigene Default-Instanz, und das ist eine andere, als die am Bildschirm angezeigte.
    Einfaches Rezept: Finger weglassen von der Defautl-Instanz.
    Daraus folgen dann unvermeidbar mehr oder weniger viele Änderungen an der Architektur - das muss dann halt in Angriff genommen werden.
    Ja, ich habe deinen Link zu Stackoverflow gelesen. Wie oben geschrieben, wollte ich aber jetzt nicht einen Edit zu deinem Edit machen, damit wir hier wieder eine saubere Reihenfolge reinkriegen. Deine Erklärung war ausgezeichnet!!! Ich werde mich jetzt erstmal ein wenig zu ThreadStatic belesen.

    Diese schwammige Positionierung von frm_Main als quasi-statische Klasse war mir bekannt, aber das jeder Thread seine eigene Instanz davon bekommt ist mir neu. Das ist ja schrecklich! Es stellt sich mir die Frage welche Konsequenzen ich daraus ziehe?!

    Spoiler anzeigen

    Um mal die Ausgangssituation zu skizzieren:
    Ich habe eine Art Baukasten für sehr spezifische ingenieurstechnische Probleme geschrieben. Die Anwender können dabei das zu betrachtende Problem selbst modellieren. Die Anwendung besteht (vereinfacht) aus vier lose gekoppelten Schichten.

    1) Eine GUI, welche die Anwender mit Hilfe von speziellen UserControls selbst gestalten können.
    2) Eine Datenbank mit technischen Kenngrößen und Kleinkram zur freien Verwendung.
    3) Das zu modellierende technische Problem ist in einer eigenen Schicht gekapselt und wird modular aus einen Sortiment von Grundklassen zusammengesetzt. Diese Schicht ist eine ganz reguläre OOP-Klassenstruktur.

    Um die drei Schichten zusammenzuhalten und (außerdem) typische Programmbasisfunktionen zu realisieren, liegt hinter der Anwendung noch eine vierte Schicht aus statischen Helferklassen.

    4) Die Helferklassen sind quasi autonom und kapseln alles was man irgendwie so braucht: DataBinding zwischen den Schichten, Fehlermanagement, Validierungsmethoden, Ein-/Ausgabeüberwachung, Dateioperationen, Mehrsprachigkeit, Anwendungshilfe, Anbindung an ein externes CAD-Programm zur Visualisierung usw. Sie sind das Herz des Baukastens und machen ca. 12.000LOC aus.

    Die Grundidee war, den Kollegen (Ingenieure, keine Programmierer!) eine vollwertige Programmiersprache mit strikter Typisierung und OOP zur Modellierung ihrer Probleme zur Verfügung zu stellen, ohne das sie sich mit den Details von Anwendungsentwicklung auseinandersetzen müssen. Auch wenn man es kaum glauben mag (und ich in diesem Thread wie der letzte Vollhorst rüberkomme), funktionierte das bis gestern sogar ziemlich gut.



    Ich bewege mich eigentlich immer im Hauptthread. Um Daten aus meiner Anwendung in das angebundene CAD-Programm zu schaufeln, wollte ich auf einen BGW umsteigen. Ansonsten steht die Anwendung für ca. 1 Minute still. Das kommt allerdings auch nicht häufig vor, braucht man evtl 1 mal pro Stunde.

    Ich könnte aus meinen statischen Helferklassen reguläre Klassen machen. Das heißt alles noch einmal anpacken zu müssen. Das stellt ein richtig großes Problem dar! Ich habe diese ganzen Hilfsklassen in der Objekthierarchie direkt neben frm_Main positioniert. D.h. die etablierten OOP-Gepflogenheiten (z.B. zur Kommunikation von Owner/SubObject) habe ich ignoriert. frm_Main und die Helferklassen können frei aufeinander zugreifen. Das stellte bislang keine Schwierigkeit dar, weil frm_Main und die Helfer halt zusammen die Programmbasis bilden und nicht unabhängig voneinander funktionieren sollen. Aber die Konsequenzen dieser Änderung, vor allem was die Interklassenkommunikation angeht, kann ich noch gar nicht überschauen.

    Alternativ lebe ich mit der Ist-Situation. In diesem Projekt stecken 4 Jahre Arbeit und es funktioniert ja (fast) alles wie gewünscht.

    Was tun? Welche Optionen bleiben?

    PS: Hallo EDR :)

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

    bei 12000 LOC, wenn da durchgehend mit der Standard-Instanz geschmuddelt wurde sehe ich garkeine Option mehr, den Schmuddel noch zu "reinigen".



    Übrigens der Code in post#1 ist auch ein mutmassliches Trauerspiel, nämlich diese Zeile :

    VB.NET-Quellcode

    1. Return CType(FIRST_DataSet.dt_Settings.Rows(0).Item("Language"), String)


    würde die nicht auch typisiert funktionieren?, also einfach

    VB.NET-Quellcode

    1. Return FIRST_DataSet.dt_Settings(0).Language
    Probier ma aus.



    Ich hab übrigens iwo noch eine Klasse, mit der kann man jeden Aufruf aussm NebenThread in den HauptThread delegieren, aber um die zu nutzen, muss man anonyme Methoden formulieren können.
    Ah, hier: AsyncWorker
    Ist vlt ganz geschickt, insbesondere, wenn du deine Backgroundworker dafür wegschmeisst, die sind nämlich ähnlich gräuselig wie ein untypiseirtes Dataset.

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

    Zum DataSet: Ohja, da habe ich mir einen abgekrampft. Ich habe mal ne Suche durch mein Projekt laufen lassen. Alle anderen Zugriffe mache ich ohne Konvertierung. Keine Ahnung welchen Aussetzer ich da hatte.

    Zum AsyncWorker: Um dieses Tutorial schleiche ich bereits seit ein paar Tagen herum. Ich konnte mich bislang aber erst dazu aufraffen, dass von @Nikx zum BGW von Niko Ortner zu machen. Ich werde es mir auf jeden Fall anschauen!

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

    Ein Tutorial zum AsyncWorker von mir? Hab ich was verpasst?

    Grüße
    "Life isn't about winning the race. Life is about finishing the race and how many people we can help finish the race." ~Marc Mero

    Nun bin ich also auch soweit: Keine VB-Fragen per PM! Es gibt hier ein Forum, verdammt!