Inirem
Inirem

Reputation: 61

Blazor SignalR Create a List for all clients but only update it once

I just worked my way through this MS Learn Tutorial regarding SignalR in Blazor.

At the end of the tutorial, you get a program that can have multiple clients hooked up to a "ChatHub" to send and receive messages, like a "Townsquare-Chatroom"

While testing I realized, that if you send some messages and afterward create a new client, the new client does not display the previously send messages. This is because every client stores its received messages locally as shown here:

@code{
// ...
private List<string> messages = new();
// ...
}

I decided to implement such a feature. To do so, I created ChatLog.cs which is supposed to log the messages for all clients instead of saving them inside of each individual client:

public class ChatLog
{
    private List<string> _messages= new List<string>();

    public List<string> Messages
    {
        get { return _messages; }
        set 
        { 
            _messages = value;
        }
    }
}

Of course, I also had to make some changes inside of index.razor to make things work:

  1. I added a new service in program.cs as singleton

==> Program.cs

// ...
builder.Services.AddSingleton<ChatLog>();
// ...

and injected ChatLog into my index.razor

==> Index.razor

// ...
@inject ChatLog ChatLogger
// ...
  1. I changed the code in index.razor @code to add the messages to ChatLog.Messages instead of the "local" messages-List
protected override async Task OnInitializedAsync()
    {
        // Change
        if(ChatLogger.Messages is null)
        {
            ChatLogger.Messages = new();
        }

        hubConnection = new HubConnectionBuilder()
            .WithUrl(NavManager.ToAbsoluteUri("/chathub"))
            .WithAutomaticReconnect()
            .Build();

        hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
        {
            var formattedMessage = $"{user}: {message}";
            // Change
            ChatLogger.Messages.Add(formattedMessage);
            InvokeAsync(StateHasChanged);
        });

        await hubConnection.StartAsync();
    }

Now I run into a new problem.

Since the event

hubConnection.On<string, string>...

is called by every client, and all new messages get added into ChatLog.Messages X-times (x == amount of active clients).

I just can't think of a way to avoid this problem and only log every message exactly once. Can someone help me?

Thanks in advance and sorry for the long explanation. Maybe someone can also help shorten it?

EDIT To clarify the problem: Since the messages get added to the messages List inside of the event (as shown above), every instance (or every tab of the website) adds the message, resulting in multiple (and unwanted) adds.

E.g.

enter image description here

Upvotes: 2

Views: 384

Answers (1)

akseli
akseli

Reputation: 2848

From what I can gather this is more a learning exercise than something you're actually planning on using in a production environment, so we can ignore the fact that this isn't really a very robust implementation.

In any case, a simply solution would be to have the sender of the message store it in the messagelog, instead of storing it upon reception.

Taking from the tutorial you followed:

using Microsoft.AspNetCore.SignalR;

namespace BlazorServerSignalRApp.Server.Hubs
{
    public class ChatHub : Hub
    {
        public async Task SendMessage(string user, string message)
        {
            // STORE YOUR MESSAGE IN YOUR MESSAGE LOG HERE
            await Clients.All.SendAsync("ReceiveMessage", user, message);
        }
    }
}

You should be able to inject your MessageLog service into the ChatHub in order to access it from there. (If I'm understanding your project structure correctly)

Upvotes: 1

Related Questions