C# EventHandler feuert mehr als einmal?

Thommy1972de

Newbie
Registriert
Apr. 2024
Beiträge
4
Hallo. Ich habe ein Problem an dem ich jetzt schon ein paar Tage hänge und ich komme einfach nicht weiter.
Ich erhalte aus einer dll einen Eventhandler. an der Zahl sind das 5 oder 6 aber beim ersten gibts schon Probleme.
Das Event wir innerhalb von Mikrosekunden 2-3 abgefeuert. In diesem Event wird ein Datenbankeintrag erstellt und damit habe ich den dann 2x dring was Fatal ist.
Gibt es eine Möglichkeit einem Eventhandler zu sagen, dass er nach dem 1. mal stoppen soll?
1713704813783.png
 
Ist jetzt vielleicht nicht die "Clean Code" Lösung. Aber kannst du nicht beim Aufruf den Event Handler abmelden, um zu verhindern, dass er erneut aufgerufen wird.

Oder Kann und soll sich der Aufruf wiederholen können?
 
Ich habe es mit einer direkten MySQL Connection versucht, mit RestSharp, mit Action Delegaten, mit Thread.Sleep(xx), mit Booleans, habe das Object nach der Überghabe gelöscht und was weiß ich noch alles. Selbst bei Thread.Sleep() wartet er nur xx Millisekunden und trägt die Daten dann 2x ein. Ich habe keine Ahnung warum gerade 2x und nicht 4x oder 10x....
Ergänzung ()

mattsavvy schrieb:
Ist jetzt vielleicht nicht die "Clean Code" Lösung. Aber kannst du nicht beim Aufruf den Event Handler abmelden, um zu verhindern, dass er erneut aufgerufen wird.

Oder Kann und soll sich der Aufruf wiederholen können?
Das Event selbst brauche ich ständig. Es darf aber pro Aufruf nur einen Eintrag machen
 
Wenn es ein Bug in der verwendeten Library ist, kannst du ja versuchen den zu fixen. Ansonsten gibts nur Workarounds. Rufst du den Code asynchron auf? Falls nicht, würde ich einfach checken, ob die DB bereits die Daten von diesem Event hat, wenn ja, ignorier das Event einfach, wenn nein, dann schreibs in die DB
 
Events sollten im Normalfall nicht mehrmals innerhalb von Mikrosekunden feuern, es sei denn, sie werden tatsächlich mehrmals ausgelöst. Schau mal, ob der Eventhandler nicht versehentlich mehrmals hinzugefügt wird oder ob die Bedingungen, die das Event auslösen, nicht zu schnell aufeinander folgen.

Es wäre auch hilfreich, mehr vom Kontext des Codes zu sehen.

Ansonsten könntest du es auch mit 'SemaphoreSlim' versuchen:


C#:
using System;
using System.Threading;
using System.Threading.Tasks;

public class SomeEventHandlerClass
{
    private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
    private bool isHandled = false;

    private async void EventHandler(object sender, EventArgs e)
    {
        // Warte, um den Semaphore zu bekommen. Wenn isHandled schon true ist,
        // machen wir direkt weiter, ohne etwas zu tun.
        await semaphoreSlim.WaitAsync();

        try
        {
            if (isHandled)
            {
                return;
            }

            // Setze dein Flag und bearbeite das Event.
            isHandled = true;

            // Hier kommt dein Code...

            isHandled = false;
        }
        finally
        {
            // Nicht vergessen, den Semaphore freizugeben!
            semaphoreSlim.Release();
        }
    }
}

Du kannst auch einen 'Cooldown' einbauen:


C#:
using System;
using System.Threading;
using System.Threading.Tasks;

public class EventDebouncer
{
    private static readonly TimeSpan DebounceTime = TimeSpan.FromMilliseconds(200); // 200ms debounce Zeit
    private DateTime lastHandled = DateTime.MinValue;

    private void DebouncedEventHandler(object sender, EventArgs e)
    {
        if (DateTime.Now - lastHandled < DebounceTime)
        {
            // Wenn das letzte Event zu kürzlich war, ignorieren wir dieses hier.
            return;
        }

        // Aktualisiere den Zeitpunkt der letzten Ausführung.
        lastHandled = DateTime.Now;

        // Führe deine Logik hier aus...
    }
}
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: Raijin, sandreas und areiland
Warum machst du denn bei jedem Event die Datenbankverbindung neu auf? Da freut sich der Server aber bestimmt nicht drüber...

Ich würde mir an deiner Stelle auch mal Log-Ausgaben dort machen, wo die Events behandelt werden. Mit nem Debugger kommst du hier vermutlich nicht weit, wenn die Events parallel und so schnell hinter einander gesendet werden.
 
sandreas schrieb:
Warum machst du denn bei jedem Event die Datenbankverbindung neu auf? Da freut sich der Server aber bestimmt nicht drüber...
Jein. Zum einen sehen wir nicht was genau denn hinter dbCon steckt und zum anderen muss nicht zwingend jedes Open() und Close() tatsächlich auch genau das tun was der Name impliziert. SqlConnection zieht sich beispielsweise eine offene Verbindung aus dem Connection Pool, wenn eine verfügbar ist.

The SqlConnection draws an open connection from the connection pool if one is available. Otherwise, it establishes a new connection to an instance of SQL Server.

Meistens verwendet man daher die SqlConnection in einem using-Statement, weil dort das Connection Handling mehr oder weniger automatisch erfolgt. Wenn man das zu Fuß programmiert und da wirklich andauernd echtes Open() und Close() gemacht wird, kann das natürlich Performance kosten, keine Frage.


Thommy1972de schrieb:
Das Event wir innerhalb von Mikrosekunden 2-3 abgefeuert.
In dem Fall drängt sich die Frage auf warum das so ist. Was ist das für eine dll und warum feuert sie den Event so oft? Soll das so dein?

Prinzipiell ist jedes Event eigenständig, eine eigene Instanz. Wenn du zB 38 Mal pro Sekunde auf einen "Add" Button hämmerst, wird 38x das Click-Event abgefeuert. Selbst wenn du dort e.handled=true setzen würdest, gälte das nur für diese eine Event-Instanz, aber die anderen 37 Instanzen sind nach wie vor gefeuert und wollen behandelt werden.

Möchtest du nun mehrere gefeuerte Events gegeneinander abriegeln, musst du entweder den Handler entsprechend absichern (zB mit einer Semaphore wie @mattsavvy es zeigt) oder du kannst vor dem Insert auf Vorhandensein des Records prüfen. Auch eine Event-"Pause" wie du sie mit der DebounceTime gebaut hast ist eine Möglichkeit. Dann sollte die Prüfung allerdings wie @marcOcram schon geschrieben hat ganz oben stattfinden.

Letztendlich ist das aber nur Symptombekämpfung. Entweder sollte das Event gar nicht so oft gefeuert werden oder aber das Event ist eigentlich anders zu handhaben.



dms schrieb:
@Thommy1972de und bitte Prepared SQL bauen
Der Hinweis ist sehr wichtig! @mattsavvy, dein Code ist zur Zeit anfällig gegenüber SQL-Injection. Wie in dem verlinkten Comic dargestellt kann ein böswilliger Anwender bei Eingabefeldern SQL-Statements einschleusen, wenn diese nicht validiert werden. Baut man ein SQL-Statement als String zusammen, kann genau das passieren. Deswegen solltest du dir angewöhnen, einen Parameterized Query zu bauen. Das ist typsicher und niemand kann ein "DROP ALL TABLES" einschleusen, höchstens als reinen String, aber ohne Funktion.
 
  • Gefällt mir
Reaktionen: abcddcba
Zurück
Oben