Singleton Blazor und eine Art von NotifyPropertychanged

Gestern war ich bei der .net usergroup in Hamburg. Im Gepäck ein Blazor Freeride Vortrag. Es kamen viele sehr gute Fragen auf, bei einer bin ich ins Stocken geraten. Im Kern geht es darum, wie ein Objekt seine UI benachrichtigen kann. Also praktisch das, was in MVVM ein INotifypropertyChanged macht. Allerdings erschwerend in einer Blazor Server App mit einem Singleton DI Objekt. Praktisches Anwendungsbeispiel ein Chat. Dieser existiert nur einmal (als Liste) und beim Update müssen alle “subscriber” informiert und deren Web User Interface neu geschrieben werden. Bisher liest man da von Lösungen, die auf einem separaten SignalR Hub aufsetzen.

Dieses C# Beispiel reduziert auf eine Zahl als Eigenschaft, die allen konnektierten Anwendern zur Verfügung stehen soll.

Dazu wird in der Startup.cs ein Objekt dem Dependency Injection Container zugefügt.

   1:  services.AddSingleton<SharedModel>();
   2:       

In der Blazor Component Page wird das Objekt das per DI injiziert (Referenz)

   1:  @page  "/sm"
   2:  @using  BlazorUSA.Models
   3:  @inject SharedModel sm
   4:   
   5:  @sm.Zahl
   6:  <button @onclick="add">add</button>

Der spannende Teil der Model Klasse fehlt noch. Eine Property vom Typ integer wird hochgezählt und in der Page angezeigt. Um der Page eine Änderung mitzuteilen und das rendering zu erneuern ruft man StateHasChanged auf. Damit wird der Rendervorgang des Shadow Dom angeworfen. Aber wir sind in einer Klasse und nicht Page. Deshalb bekommt diese Klasse ein Event, angelehnt an WPF/UWP/XAML PropertyChangedEvent bezeichnet. Man könnte im Setter des Propertys das Event feuern oder wie hier eine extra Methode PropertyChanged anlegen. Diese wird dann in der Page explizit aufgerufen.

   1:  public class SharedModel
   2:  {
   3:    public int Zahl { get; set; }
   4:   
   5:    public  event EventHandler PropertyChangedEvent;
   6:    public async void PropertyChanged()
   7:    {
   8:      PropertyChangedEvent?.Invoke(this, EventArgs.Empty);
   9:    }
  10:  }

Nun zur Magie in der Page Logik. In der Initalisierungsphase wird das Event meiner Singleton Klasse abonniert. Da man hier in ein Threading Problem reinläuft, wird noch einmal ein Dispatching mit InvokeAsync der Page zwischen geschalten. Dadurch wird dann der Renderprozess der Page mit StateHasChanged initiert. Um ehrlich zu sein, ich hasse Entwickler die Code wie Zeile 6 schreiben. Praktisch entspricht dies in etwa einer Methode ala   
void StateChange(object s, EventArgs e) =>  InvokeAsync(StateHasChanged);

Wenn ein Benutzer nun den Add Button drückt, wird inkrementiert und das Benachrichtigungs Event PropertyChanged aufgerufen.

   1:  @code {
   2:  protected async override Task OnInitializedAsync()
   3:      
   4:  {
   5:   
   6:     sm.PropertyChangedEvent +=(_,__)=>InvokeAsync(StateHasChanged);
   7:  }
   8:  public void add()
   9:  {
  10:     sm.Zahl++;
  11:    sm.PropertyChanged();
  12:  }
  13:   
  14:   
  15:  }

So erhalten alle Benutzer dieser Blazor Server  Web App, wenn sie diese Page offen haben, immer den aktuellen Wert von Zahl.

Kommentare sind geschlossen