Get a list of all connected users to a Blazor application
Asked Answered
L

2

11

So for my blazor application is currently in production.

I do need to release new versions at some points. I would like to know if at that time anyone is using the application. For this I could ask everyone (about 20 colleagues) whether anyone is using the application.

I feel like there should be an easier way to do this. Now I know that a blazor application uses SignalR to perform the communication between server and client and that when I restart the docker container, all connections are reset. So I feel like there should be a way to request from the SignalR Hub whether anyone is connected to the application, but I have not yet found a way.

Does anyone know any way of checking if (and who) is using the application at point x in time?

Lunkhead answered 23/11, 2022 at 13:59 Comment(2)
Have looked into this answer.Elka
@Elka That indeed looks like what I'm looking for, will have a look in the morning. Thank you.Lunkhead
B
9

The easy way is to use Dependency Injection. On Blazor server, scoped, almost means "one instance by connection":

The Blazor Server hosting model supports the Scoped lifetime across HTTP requests but not across SignalR connection/circuit messages among components that are loaded on the client. The Razor Pages or MVC portion of the app treats scoped services normally and recreates the services on each HTTP request when navigating among pages or views or from a page or view to a component. Scoped services aren't reconstructed when navigating among components on the client, where the communication to the server takes place over the SignalR connection of the user's circuit, not via HTTP requests. In the following component scenarios on the client, scoped services are reconstructed because a new circuit is created for the user:

  • The user closes the browser's window. The user opens a new window and navigates back to the app.
  • The user closes a tab of the app in a browser window. The user opens a new tab and navigates back to the app.
  • The user selects the browser's reload/refresh button.

If it's acceptable for you to regard a lifetime SignalR connection/circuit as a "connected user", the process is quite straightforward. I explain the process step by step:

1 - Creating structs to store list of connected users

Create classes to store ConnectedUser data and a ConnectedUserList:

public class ConnectedUser
{
    public string Name { get; set; } = Guid.NewGuid().ToString();
}
public class ConnectedUserList
{
    public List<ConnectedUser> Users { get; set; } = new();
}

2 - Inject classes with the right scope

On program.cs inject both classes on services: ConnectedUser (scoped) and ConnectedUserList (singleton) that will contain a list of ConnectedUser.

// Program.cs
builder.Services.AddScoped<ConnectedUser>();
builder.Services.AddSingleton<ConnectedUserList>();

3 - Capture connected user and add to global list

On Shared/MainLayout.razor, get by injection both objects and add ConnectedUser to ConnectedUserList (on login process or events, you can update ConnectedUser.Username to the logged username)

@implements IDisposable
@inject ConnectedUser _ConnectedUser
@inject ConnectedUserList _ConnectedUserList

@code {

    protected override void OnInitialized()
    {
         _ConnectedUserList.Add(_ConnectedUser )
    }

    // Don't forget to remove `ConnectedUser` from 
    // `ConnectedUserList` on dispose.
    public void Dispose()
    {
         _ConnectedUserList.Remove(_ConnectedUser )
    }

4 - Show all connected users

At any moment you can get ConnectedUserList and check the list. For example index.razor:

@page "/"

@inject ConnectedUser _ConnectedUser
@inject ConnectedUserList _ConnectedUserList

<PageTitle>Index</PageTitle>

<h1>Hello, world!</h1>

@foreach (var user in _ConnectedUserList.Users)
{
    <p>@user.Name</p>
}

Notes

  • To keep the answer simple, I used a List struct to store connected users, but List is not thread safe.
Buggy answered 23/3, 2023 at 17:7 Comment(4)
In Blazor Server .net 7, MainLayout.razor is based on LayoutComponentBase which doesn't implement IDisposable - Dispose() 0 CS0115: 'MainLayout.Displse()': no suitable method found to override. I had hoped this would work instead of a CircuitHandler!Viscera
hi @JClarkCDS, please, follow instructions step by step. Also check Dispose name, you wrote MainLayout.Displse(). As you say, by default, MainLayoutdoesn't implement IDisposable, but, with posted code, it does (search by public override void Dispose() on this answer)Buggy
That was a spelling mistake in my comment. The error is CS0115: 'MainLayout.Dispose()' with public override void Dispose() having the red underline. I followed the instructions step by step and get the compile error. When researching the CS0115, the only reason I could find was the base component doesn't implement IDisposable.Viscera
@Viscera ok, I understand, I removed override can you try again?Buggy
T
1

The posted code snippet in MainLayout.razor is wrong. It is missing a reference to the Users List in the _ConnectedUsersList service. Try this instead:

        protected override void OnInitialized()
        {
             _ConnectedUserList.Users.Add(_ConnectedUser );
        }
Trabue answered 6/11, 2023 at 3:25 Comment(1)
Thank you for being a contributor to the Stack Overflow community. As a question and answer site, Stack Overflow works differently than most message boards. As such, kindly reserve the Answers feature for actual answers, not follow-up questions or comments.Cuss

© 2022 - 2024 — McMap. All rights reserved.