2

I'm working on a Blazor Server app and need to detect when a user actually leaves the application (e.g. closes the browser/tab or loses connection), not just when they navigate between pages.

As you know, each navigation within a Blazor Server app tears down the current circuit and opens a new one — which also triggers OnConnectionDownAsync() and OnConnectionUpAsync() in a custom CircuitHandler. This makes it hard to know whether the user simply navigated or truly disconnected. To solve this, I’m trying the following approach:

Proposed approach: Use a timeout in OnConnectionDownAsync to delay any "user disconnected" logic, and cancel the timeout if a new circuit connects shortly afterward. Here’s the code:

private readonly ConcurrentDictionary<string, Timer> _disconnectTimers = new();
private readonly ConcurrentHashSet<string> _activeCircuits = new(); // or use ConcurrentDictionary if needed

public override Task OnConnectionDownAsync(Circuit circuit, CancellationToken cancellationToken)
{
    var timer = new Timer(_ =>
    {
        // Check if user has not reconnected
        if (!_activeCircuits.Contains(circuit.Id))
        {
            _userTracker.MarkUserAsOffline(circuit.User);
        }
    }, null, TimeSpan.FromSeconds(5), Timeout.InfiniteTimeSpan);

    _disconnectTimers[circuit.Id] = timer;

    return Task.CompletedTask;
}

public override Task OnConnectionUpAsync(Circuit circuit, CancellationToken cancellationToken)
{
    _activeCircuits.Add(circuit.Id);

    if (_disconnectTimers.TryRemove(circuit.Id, out var timer))
    {
        timer.Dispose();
    }

    _userTracker.MarkUserAsOnline(circuit.User);

    return Task.CompletedTask;
}

Question: Does this approach make sense for distinguishing between user navigation and actual disconnects in Blazor Server?

Are there any edge cases or race conditions I should watch out for?

Is there a better pattern for handling this kind of circuit tracking?

I’d appreciate feedback or improvements from anyone who’s dealt with similar scenarios.

3
  • I'm assuming that you've seen that a new circuit gets created on navigation, but it's my understanding that the circuit persists when navigating within the same app in the same browser tab. What method are you using to navigate the user to new pages within the app? Commented May 15 at 15:59
  • It seems that this Issue was caused because I used the MudBlazor library. Specifically I used the MudNavLink component and did not set the ForceLoad to false. BUT: the exact same problem is still present for 404 errors. When the app shows my 404 page, thecircuit closes and a new one opens. Commented May 19 at 13:53
  • Good find! I think you should update your question to re-focus it on the 404 navigation specifically now. I'm guessing it has something to do with 404 routing happening at the server rather than the client. Commented May 19 at 14:12

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.