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?
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:
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();
}
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>();
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.Users.Add(_ConnectedUser );
}
// Don't forget to remove `ConnectedUser` from
// `ConnectedUserList` on dispose.
public void Dispose()
{
_ConnectedUserList.Users.Remove(_ConnectedUser );
}
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>
}
List
struct to store connected users, but List
is not thread safe.