Laden...

Wie kann ich auf das abonnieren eines Events reagieren?

Erstellt von STF-DIR vor 5 Jahren Letzter Beitrag vor 5 Jahren 1.893 Views
S
STF-DIR Themenstarter:in
368 Beiträge seit 2006
vor 5 Jahren
Wie kann ich auf das abonnieren eines Events reagieren?

Hallo,

ich habe eine Klasse, die ein Event zur Verfügung stellt.
Beim instanzieren der klasse wird in deren Konstruktor eine Methode aufgerufen,welche prüft ob eine Verbindung zu einem Dienst verfügbar ist oder nicht.
falls nicht wird ein Timer gestartet, der dann immer wieder prüft ob die Verbindung hergestellt werden kann oder nicht.
Dabei wird dann das event gefeuert und in meiner Form wird dann ein Panel rot oder grün.

Soweit so gut, wenn die Verbindung beim starten ( instanzieren der Klasse ) noch nicht hergestellt werden kann funktioniert das ganze super.
Allerdings wenn die Verbindung beim instanzieren hergestellt werden kann wird ja der Timer gestoppt, und somit das event nicht gefeuert. ( mein statuspanel nicht aktualisiert )
Der Timer läuft erst wieder an, wenn eine Methode in der klasse aufgerufen wird und keine Verbindung mehr besteht.

In meiner Form instanziere ich also die klasse und rufe den Konstruktor auf, und DANACH abonniere ich aber erst das event.
Also wird das Statuspanel beim instanzieren der Klasse nicht aktualisiert, wenn schon eine Verbindung hergestellt wurde.

Mir fehlt jetzt ein bisschen der Ansatz wie man das lösen könnte, da der Timer ja auch nicht immer laufen soll.

Habt ihr eine Idee wie man das lösen kann ?

Matthias

16.806 Beiträge seit 2008
vor 5 Jahren

Beim instanzieren der klasse wird in deren Konstruktor eine Methode aufgerufen,welche prüft ob eine Verbindung zu einem Dienst verfügbar ist oder nicht.

Das sollte man tunlichst nicht tun.

Eine Verbindung ist fast immer eine asynchrone Kommunikation.
Ein Konstruktor ist aber nicht asynchron, kann er auch nie sein.

Daher gehört sowas - insbesondere auch aufgrund SoC und der Testbarkeit - niemals in einen Konstruktor sondern in eine Methode.

Insgesamt ist das auch eher ein Problem des Code Designs, nicht der Ursache eines Events.
Ergo:

  • Konstruktor macht nur das, wofür er da ist: eine Instanz erstellen
  • Dann abonnierst Du die Events
  • Dann prüfst Du die Verbindung

Natürlich sollte die Klasse auch einen State halten.
Insgesamt ist das alles in wenigen Zeilen zB mit Reactive Extensions umsetzbar.

Könnte zB so aussehen (hier im Editor geschrieben, daher keine Garantie).

public class HttpEndpointChecker : IDisposable
{
    public string Uri {get;}
    public TimeSpan Interval {get;}

    public bool IsRunning {get { return !(_trigger is null); } }
    public bool IsAvailable{get;set;}

    private IDisposable _trigger;

    public HttpEndpointChecker(string uri, TimeSpan interval)
    {
        Uri = uri;
        Interval = interval;
    }

    public bool Start()
    {
       if(IsRunning) return false;

        _trigger = Observable
            .Interval(Interval)
            .Select(async _ => await VerifyAsync())
            .Subscribe();

        return true;
    } 
    public bool Stop()
    {
       if(!IsRunning) return false;

        _trigger.Dispose();
        _trigger = null;

        return true;
    }

    private async Task VerifyAsync()
    {
        var httpRequest = await ...;
        IsAvailable = httpRequest.Succeeded;
    }

    public void Dispose()
    {
        if(!(_trigger is null))
        {
            _trigger.Dispose();
            _trigger = null;
        }
    }
}
S
STF-DIR Themenstarter:in
368 Beiträge seit 2006
vor 5 Jahren

Hallo,

danke für die Antwort, dann mache ich das so.

Ich habe von Euch schon öfter den Hinweis bekommen das mein Code Designs nicht besonders ist.
Da ich mir das bisschen Wissen selber beigebracht habe und auch nur für mich benutze, habe ich mich darum noch nie so richtig gekümmert.

Habt ihr da einen guten Quellenhinweis für gutes Code Design?

Matthias

16.806 Beiträge seit 2008
vor 5 Jahren

Code Design und Architektur ist eines der wenigen Dinge, bei denen ich auf Bücher verweisen würde.
Allgemein helfen da als Basis durchaus so Bücher wie Gang of Four, Clean Code....
Erich Gamma, Martin Fowler, Kent Beck oder zB auch Robert C Martin kann man da durchaus als Orientierung nehmen.

Ansonsten halt viel Erfahrung etc.
Wenn man die Chance hat an Code Reviews teilnehmen zu können: das hilft enorm.

Immo Landwerth überträgt regelmäßig Code Reviews zu .NET live aus den Microsoft Büros.

S
STF-DIR Themenstarter:in
368 Beiträge seit 2006
vor 5 Jahren

Hallo,

danke, ich werd mir das mal zu gemüte führen.

Ich habe mich an deinem Code versucht, bin aber leider nicht ganz weiter gekommen.
Ich hab mal ein neues Project erstellt um das mal zu testen.
Allerdings funktioniert das ganze noch nicht so ganz.
Ich versteh nicht, wie ich die Änderung im connection status abfangen kann.
Leider hab ich mit Tasks und await/async noch keine erfahrungen, aber die kann man ja auch nur so sammeln.

Ich hab das mal absichtlich alles in eine Datei geschrieben, der Einfachheit halber.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using FSUIPC;

namespace TestObserver
{
    public class FsuipcInfo
    {
        public FsuipcInfo()
        {
        }

        public async Task<bool> GetFsuipcState()
        {
            bool running = false;
            try
            {
                /// Hier wird geprüft ob eine Abfrage erfolgreich ist Wenn nicht
                /// bleibt running = false
                FSUIPCConnection.Process();
                running = true;
            }
            catch (Exception eex)
            {


            }
            return await Task.FromResult<bool>(running);
        }
    }



    public class FsuipcHandler
    {
        public delegate void FsuipcState();
        public event FsuipcState OnFsuipcStateChanged;

        public async Task<bool> AccessFsuipcAsync()
        {
            FsuipcInfo fi = new FsuipcInfo();
            Task<bool> fiTast = fi.GetFsuipcState();
            bool running = await fiTast;

            if (OnFsuipcStateChanged != null)
            {
                OnFsuipcStateChanged();
            }

            return running;
        }

    }
    public partial class Form1 : Form
    {
        FsuipcHandler fh;
        public Form1()
        {
            InitializeComponent();
            fh = new FsuipcHandler();
            fh.OnFsuipcStateChanged += Fh_OnFsuipcStateChanged;
        }

        private void Fh_OnFsuipcStateChanged()
        {
            MessageBox.Show("OnFsuipcStateChanged");
        }

        private async void getFsuipcState()
        {
            await fh.AccessFsuipcAsync();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            getFsuipcState();
        }
    }

}

Nun hab ich zwar das event abonniert, aber wenn die Verbindung nicht zur verfügung steht dann passiert nix 😦

Vielleicht kannst du da noch mal kurz drüber schauen.
Da scheint irgendwie dieser Teil


    private async Task VerifyAsync()
    {
        var httpRequest = await ...;
        IsAvailable = httpRequest.Succeeded;
    }

zu fehlen, aber ich weiß leider nicht wie ich das implementieren kann?

Matthias

16.806 Beiträge seit 2008
vor 5 Jahren

Dein Code hat ja insgesamt fast nichts mit meinem am Hut 😉

Wo wird denn bei Dir geprüft?

S
STF-DIR Themenstarter:in
368 Beiträge seit 2006
vor 5 Jahren

Hallo,

ja sorry, ich muss es ja auf die FSUIPC ummünzen.
Wie gesagt, ich hab nich nie was mit Task/async/await gemacht, aber jeder hat ja mal angefangen 😃
Geprüft wird hier:


        public async Task<bool> GetFsuipcState()
        {
            bool running = false;
            try
            {
                /// Hier wird geprüft ob eine Abfrage erfolgreich ist Wenn nicht
                /// bleibt running = false
                FSUIPCConnection.Process();
                running = true;
            }
            catch (Exception eex)
            {


            }
            return await Task.FromResult<bool>(running);
        }

Wenn FSUIPC.Process() funktioniert, dann wird running = true, ansonsten bleibt es false.
In dieser FSUIPC geschichte gibt es keine andere möglichkeit zu prüfen, ob eine Verbindung hergestellt werden kann oder nicht.

Matthias

16.806 Beiträge seit 2008
vor 5 Jahren

Vermutlich sollte es eher in folgende Richtung gehen:

    public class FsuipcInfoStateChangedArgs : EventArgs
    {
        public bool IsAvailable {get;}
        public FsuipcInfoStateChangedArgs( bool isAvailable )
        {
            IsAvailable = isAvailable;
        }
    }

    public class FsuipcInfo
    {
        public FsuipcInfo(TimeSpan interval)
        {
           Interval = interval;
        }


        public TimeSpan Interval {get;}
        private IDisposable _trigger;

        public bool IsRunning {get { return !(_trigger is null); } }
        public bool IsAvailable{get;}

        public delegate void FsuipcInfoStateChangeHandler( object sender, FsuipcInfoStateChangedArgs e );
        public event FsuipcInfoStateChangeHandler OnStageChanged;

        public bool Start()
        {
            if(IsRunning) return false;

            _trigger = Observable
                .Interval(Interval)
                .Select(isAvailable => Observable.FromAsync(async () => await IsAvailableFsuipc())
                .Subscribe(isAvailable => SetState(isAvailable));

            return true;
        }

        public bool Stop()
        {
            if(!IsRunning) return false;

            _trigger.Dispose();
            _trigger = null;

            return true;
        }

        private void SetState(bool currentState)
        {
            if(currentState != IsAvailable)
            {
               IsAvailable = currentState;
               FireStateChange(IsAvailable);
            }
        }

        private void FireStateChange(bool isAvailable)
        {
            if ( OnStageChanged != null )
            {
                OnStageChanged(this, new FsuipcInfoStateChangedArgs(isAvailable))
            }
        }

        public async Task<bool> IsAvailableFsuipc()
        {
            return await Task.Run( () =>
            {
                try
                {
                    /// Hier wird geprüft ob eine Abfrage erfolgreich ist Wenn nicht
                    /// bleibt running = false
                    FSUIPCConnection.Process();
                    return true;
                }
                catch (Exception ee)
                {
                    return false;
                }
            }).ConfigureAwait(false);
        }
    }

In diesem Falle kann man auch auf das async verzichten, wenn die Komponente async nicht unterstützt - bleibt halt Deine UI hängen und Du musst es wieder in nen Thread auslagern.
Daher hier die bessere Variante mit einem Wrap via Task.Run().

Ansonsten: mach paar Tutorials durch.
Tasks und Threads muss man verstehen.

S
STF-DIR Themenstarter:in
368 Beiträge seit 2006
vor 5 Jahren

Hallo,

ja genau, werd mal ein paar tutorials durchmachen um das ganze erst mal genau zu verstehen.
Ich danke dir bis dahin erst mal für die Hilfe !!

Eine letzte Frage hab ich aber noch.
Ich hab deinen Code oben mal in VS übernommen.
Leider meckert VS da immer an dem "Observable" rum und ich weiß nicht was ich da machen muss.
Eine fehlendes using scheint es nicht zu sein, oder doch ?
Mit dem Fehler nicht wirklich was anfangen. VS sagt nur, das Observable im aktuellen Context niht vorhanden ist... ?
Ich hab mal ein Bild davon angehanagen.

Ich wäre dir sehr dankbar, wenn du mir damit als letztes noch mal elfen könntest, denn da hing ich vorhin schon.

danke
Matthias

16.806 Beiträge seit 2008
vor 5 Jahren

Insgesamt ist das alles in wenigen Zeilen zB mit Reactive Extensions umsetzbar.

https://github.com/dotnet/reactive -> NuGet Paket.

Kannst aber auch drauf verzichten und statt dem Observerable nen Timer nehmen.

S
STF-DIR Themenstarter:in
368 Beiträge seit 2006
vor 5 Jahren

Hallo,

ach das ist ein Paket ... oh man.... ich hatte das gelesen, aber weil da "zum Beispiel" hab ich gar nicht in diese Richtung gedacht.

Danke dir !!

Matthias

16.806 Beiträge seit 2008
vor 5 Jahren

Reactive Extension wird leider viel zu wenig beachtet, obwohl es Quellcode extrem vereinfacht und Dinge ermöglicht, für die man sonst ein riesen Topf an (teilweise umständlichen) Code benötigt (und diesen natürlich auch testen sollte).
Dabei hat sich jedoch Reactive Extensions für andere Sprachen schon viel weiter verbreitet - quasi kein Angular ohne NgRX - als aktuell noch in .NET-

S
STF-DIR Themenstarter:in
368 Beiträge seit 2006
vor 5 Jahren

Hallo,

danke für den Hinweis noch mal, ich schau es mir gerade genauer an.

Matthias