Blazor question- close circuit\connection immediately after making async call

Viewed 2449

I have a blazor integration and basically, after an indeterminate period of time I see this screen:

enter image description here

Is there no way to prevent this from happening? My concern is that I'm maybe not closing circuits/connections after I have the data I need from the server-side call.

I have this in my ComponentBase class:

  protected override async Task OnInitializedAsync()
    {
         await DoStuff();
    }

which calls to this:

 private async Task DoStuff()
    {

        var results= await httpClient.GetJsonAsync<MyResultsClass>(someResultsUrl);
          ...  
        StateHasChanged();
    }

But I'm wondering if I need something after the fact (maybe need to implement IDisposable, have some kind of a handle to any created circuits, and dispose of it immediately) to make sure that it gets automatically cleaned up? That message should never show, and there is no need to keep any kind of connection open after I get the results I need. I just don't know what sort of handler\object I need that represents the Blazorconnection that is being made.

I found this link but it seems like a lot to accomplish one little thing.

Edit: after trying the proposed solution, I'm seeing slightly different behavior involving a message saying "Attempting to reconnect to server". When that doesn't work, I see "Could not reconnect to the server. Reload the page to restore functionality". Is there no way to automate this, or, at least, prevent it completely by automatically closing the connection\circuit (if it isn't needed)?

Edit 2: it would also be suitable if the reconnection happened automatically, if\where possible - if I leave my blazor page open for a long time, reconnection doesn't seem to work. But, if it were to reconnect at the moment it lost connection, I presume that it could help?

Edit 3: other alternative I'd be happy with is if the page automatically refreshed instead of a reconnection - that would work for me just fine.

Edit 4: (in response to enet's answer) - helps a lot, thanks! And I agree, it's a hack to try to forcibly close the connection. I was just hoping there was a way to gracefully close the connection, knowing that once DoStuff() is done doing what it needs to do, it can release the resources - instead of unnecessarily lingering. If this is a consequence of Blazor that I have to live with, then it's fine. I've found a way to suppress the "Hateful screen" (as you call it) that comes up which is great, but would be nice if I could clean up the "Error: Connection disconnected with error "Error: WebSocket closed with status code: 1006 ().' I've seen a lot of threads about that error, and they mostly lean to the solution of force an automatic refresh. I don't actually care if the error even happens, I just want to make sure that it will eventually free up the resources (and if it happens automatically, then I'm fine with that, too.) - would ideally like to be able to catch\ignore that error message, but if it's something I should be concerned about, then... I don't want to ignore it. Also, this issue is very easy to reproduce and I don't have to recycle my website app pool or anything like that.

3 Answers

You can also achieve it by implementing IDisposable/ IAsyncDisposable. Refer this github issue.

However, the CircuitHandler is a bit clearer and better compared to object initialization and disposal.

I would suggest to go with the solution suggested here

A circuit handler is implemented by deriving from CircuitHandler and registering the class in the app's service container. The following example of a circuit handler tracks open SignalR connections:

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Server.Circuits;

public class TrackingCircuitHandler : CircuitHandler
{
    private HashSet<Circuit> circuits = new HashSet<Circuit>();

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

        return Task.CompletedTask;
    }

    public override Task OnConnectionDownAsync(Circuit circuit, 
        CancellationToken cancellationToken)
    {
        circuits.Remove(circuit);

        return Task.CompletedTask;
    }

    public int ConnectedCircuits => circuits.Count;
}

Statup.cs

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();
}

Refer this for further details.

I found this link but it seems like a lot to accomplish one little thing.

This link has got nothing to do with the 'issue' you're facing.

Is there no way to prevent this from happening?

No, there isn't. This behavior is by design. It occurs when the connection to your Blazor Server App is lost and could not be automatically re-established, as for instance, during development time, you run your app, changes your code and save, but lo and behold, you see a screen with the message: Attempting to reconnect to the server..., and after a while it changes to: Could not reconnect to the server. Reload the page to restore functionality.

Clearly, this is an example when the connection to the server is lost, and the browser is attempting to reconnect, but to no avail, as the server has already destroyed the circuit. If you reload or refresh the page, the server creates a new circuit with an empty state; that is, the previous state (data) is lost. This is equivalent to closing and re-opening the browser.

Similarly, there might be other reasons all related to the lost of the connection, and externally (UI screen) look the same. The above example was given here because its easy to grasp and can be easily re-produced.

There are many reasons why a circuit connection is lost:

  • The server becomes unavailable (stolen, destroyed, set on fire, no electricity, etc.)

  • The server must release a disconnected circuit after a timeout

  • The server is under memory pressure.

  • Some internal issues, etc.

All this has got nothing to do with how and what you code. If you make an HTTP call, and provide a wrong url, you'll get the message "An unhandled exception has occurred. See browser dev tools for details.", at the bottom of the page. This is for you, the developer. Naturally, the connection is also lost. The culprit is the server, and the reason bad code, in which case you can update your code, and re-run your app. In production scenarios, you should of course handle exceptions to prevent the server from closing the connection.

It is important then to differentiate between the two scenarios described above. The first is mostly beyond your control, the second is not... you should write robust code...

But I'm wondering if I need something after the fact (maybe need to implement IDisposable, have some kind of a handle to any created circuits, and dispose of it immediately) to make sure that it gets automatically cleaned up?

You implement IDisposable only if you have to free resources, though implementing IDisposable in each of your app's components won't make any harm.

Why do you want to destroy circuits. Did you create them that you want to destroy them! A circuit is an object that hold your connection, app state, etc. It is not created by you, and you shouldn't try to touch it. Creating an HttpClient object and accessing a web api endpoint, may result in a runtime error, and closing of the connection to the server, unless you handle exceptions, but again it has nothing to do with the hateful screen you get... Ordinarily, you don't do anything... like "to make sure that it gets automatically cleaned up". What do you want to make cleaned up ?!

That message should never show, Remember, by design ?

and there is no need to keep any kind of connection open after I get the results I need.

What connection ? To the server ? To the Web Api ? You should do nothing of the sort.

I just don't know what sort of handler\object I need that represents the Blazorconnection that is being made.

In that case my linked answer can help, but this is not the source of the issue, and you shouldn't play with the connection object.

Is there no way to automate this, or, at least, prevent it completely by automatically closing the connection\circuit (if it isn't needed)?

By now, you should understand that closing the connection by the server, lead to this message, produced by the JavaScript SignalR code that attempts to reconnect to the server. You shouldn't try to do that, and you can't do that. Man, don't destroy your connections... Try to solve related issues, if possible, and accept this as expected behavior. This is the "price" you have to pay when you use Blazor Server App (SinalR).

it would also be suitable if the reconnection happened automatically, if\where possible - if I leave my blazor page open for a long time, reconnection doesn't seem to work. But, if it were to reconnect at the moment it lost connection, I presume that it could help?

Yes, here's a link to do that, but remember: This should be a temporary solution till the Blazor teams comes with a better solution.

refreshed instead of a reconnection

When your page is refreshed the server creates a new circuit with empty app state. When your page tries to reconnect implies to re-connection to the disconnected circuit on the server without losing app state and data

Could you please tell how often you get the 'hateful screen', is it related to viewing a given page, say, whenever you visit the Counter page and hit the increase button, etc.

Hope this helps...

I was just hoping there was a way to gracefully close the connection, knowing that once DoStuff() is done doing what it needs to do, it can release the resources - instead of unnecessarily lingering.

Yes, code as this close the connection (JavaScript, SignalR):

  if (connection) {
    connection.stop();
  }

But why do such a thing?

This is not windows desktop app, this is a web app running via SignalR, enabling communication through a circuit connection. Please, don't try to code like this. Your connection should close gracefully when the user closes the web page. Understand that closing a connection and then opening a new connection result in lost of the app state and the data created for the page.

What resources do you want to release after DoStuff() has run ? Do you use external resources that you must release ? And if you do, this can easily be done by implementing the IDisposable interface. And this is mostly done to prevent memory link, as Blazor constantly create and destroy components.

but would be nice if I could clean up the "Error: Connection disconnected with error "Error: WebSocket closed with status code: 1006 ().'

I guess this is doable...

I've seen a lot of threads about that error, and they mostly lean to the solution of force an automatic refresh

Exactly... because they want an active and live circuit connection. But you want to kill your connection.

I just want to make sure that it will eventually free up the resources

Again RESOURCES ??? I'm trying to understand you ? DO YOU MEAN THE RESOURCES USED BY THE SERVER ? Because perhaps of scarcity of resource ? If yes, then my answer is positive. This is from the docs:

The server can't retain a disconnected circuit forever. The server must release a disconnected circuit after a timeout or when the server is under memory pressure.

So you can rest assured that resources are going to be released without mercy...

Related