0

I'm just starting out on the web and this time I'm having trouble with event handling. I develop with blazor, but this seems like a universally applicable problem.

Where I'm stuck is this. I want to notify the page when I get a message. I had no problem with this when I had one browser screen, but when I test my web with two screens open, the events don't work properly. how do I fix this?

Below is my code.

ServiceInfoReceiveHandler.cs
public event EventHandler<(string contentType, string serverName, string message)> InstallServiceInfoReceived;
...
public void Receive(BasicDeliverEventArgs eventArgs)
{
  if (contentType == "SVCMGMT/INSTALL/SVCINFO")
  {
    InstallServiceInfoReceived?.Invoke(this, (contentType, serverName, message));
    ...
  }
}

I fire the event like this and the receive function is always called whenever a message comes in. It's not a matter of the message not arriving.

page.razor

@inject ServiceInfoReceiveHandler _serviceInfoReceiveHandler

protected override async Task OnInitializedAsync()
{ 
  _serviceInfoReceiveHandler.InstallServiceInfoReceived += OnServiceInfoReceived;
}
private async void OnServiceInfoReceived(object sender, (string contentType, string serverName, string message) e)
{
...
}

The razor page I want the result of is like above, but OnInfoReceived is not called.

beginner
  • 47
  • 7

1 Answers1

1

Here's a simple demonstration of the Blazor Notification pattern used to communicate between pages with events.

First a notification service.

namespace SO76170542.Data;

public class HelloService
{
    public event EventHandler<HelloEventArgs>? HelloChanged;

    private string? _hello;

    public string HelloMessage => _hello ?? "No Message Set";

    public void NotifyHelloChanged(string? hello)
    {
        _hello = hello;
        this.HelloChanged?.Invoke(this, new(hello));
    }
}

public class HelloEventArgs : EventArgs
{
    public string? Hello { get; set; }

    public HelloEventArgs(string? hello)
        => Hello = hello;
}

Registered in Program.

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddSingleton<HelloService>();

var app = builder.Build();

And a page to generate and consume messages.

@page "/"
@inject HelloService helloService

<PageTitle>Index</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

<div class="m-2">
    <button class="btn btn-dark" @onclick=this.UpdateHello>Update Hello</button>
</div>

<div class="alert alert-primary">@helloService.HelloMessage</div>

@code {

    // Register the event handler
    protected override void OnInitialized()
        => this.helloService.HelloChanged += this.OnHelloChanged;

    private Task UpdateHello()
    {
        helloService.NotifyHelloChanged($"Hello Blazor at {DateTime.Now.ToLongTimeString()}");
        return Task.CompletedTask;
    }

    public void OnHelloChanged(object? sender, HelloEventArgs e)
        => InvokeAsync(this.StateHasChanged);

    // We must de-register the even handler
    public void Dispose()
        => this.helloService.HelloChanged -= this.OnHelloChanged;
}

You can see the same pattern applied to FetchData here: How can I trigger/refresh my main .RAZOR page from all of its sub-components within that main .RAZOR page when an API call is complete?

enter image description here

MrC aka Shaun Curtis
  • 19,075
  • 3
  • 13
  • 31
  • Thank you for your kind response. I haven't tried it on my existing code yet, but based on your example, it looks like you set the HelloService as a singleton, which is different from mine. I set it as scoped, but is it essential to set it as a singleton? – beginner May 05 '23 at 04:31
  • If you set it as scoped then the instance will only exist for the scope of the Hub SPA session. In my image above, you would have 4 instances, one in each session, which I think is what you are observing, but don't want, – MrC aka Shaun Curtis May 05 '23 at 07:20
  • I still need to test more, but changing to singleton seems to have solved the problem. However, even if I change the example you posted to scoped, the value changes in the window where the button is pressed, which is the result I want. Is it possible that the reason I'm having trouble is because I'm not calling the event directly through the button, but through the message broker when loading the page, and when the response comes, it flows to another object (another window) in the process of firing the event? – beginner May 08 '23 at 01:06
  • Do you only want to receive the Message into one screen. Where do the messages come from? – MrC aka Shaun Curtis May 08 '23 at 13:44
  • My message is delivered via RabbitMQ from the client. I don't necessarily need to receive it on one screen, and I think implementing it as a singleton the way you did would work, but I'm not familiar with singletons, so I'm just wondering if there's a problem I'm not aware of. So I was wondering why in your example, when it's scoped, I get the event on that screen when I press the button, but in my old code above, I get it on a random screen. – beginner May 08 '23 at 23:49
  • I can't really tell because I can't see enough code. But the behaviour suggests you're implementation of the transfer of the message from the MQ is only passing the message to a single registered handler. Unicast rather that multicast. Check how you are registering and put in a break point where you invoke the event and check how many registrations you have on that event. – MrC aka Shaun Curtis May 09 '23 at 06:58
  • I've modified my code in the body a bit. I had previously created a ServiceInfoReceiveHandler object with addscoped. Through debugging, I confirmed that the passed message was never missed and always reached Receive(). However, sometimes I had a problem because OnServiceInfoReceived() was not executed after InstallServiceInfoReceived?.Invoke(). This problem was solved when I changed it to a singleton, as I keep saying, but I don't know if I can use it as it is because I have concerns about using singletons. – beginner May 09 '23 at 07:42
  • And I'm wondering why when I scoped both HelloService and my example, only my program is sending events to a random window(tab). In HelloService, when I click on the button in the window(tab), it's as if I've selected that screen, so it goes to the screen I want? – beginner May 09 '23 at 07:59