我是一个新用户,正在努力从ASP.NET Core Blazor服务器页面优雅地关闭辅助signalR客户端。
我在第一次渲染Blazor服务器页面时设置了一个辅助signalR客户端连接。当页面通过浏览器选项卡关闭时,我正在尝试关闭此辅助signalR客户端连接。
在写这篇文章的时候
似乎不会在通过浏览器选项卡关闭页面时触发。但是,
方法
是
触发了。此外,在Safari 13.0.5中,
方法是否在关闭浏览器选项卡时不被触发?Opera、Firefox和Chrome都有
在关闭浏览器选项卡时触发。通过macOS Catalina v10.15.7将Safari更新到v14.0 (15610.1.28.9,15610)修复了此问题。
目前,我正在打电话给
来自
若要关闭signalR连接,请执行以下操作。我使用以下代码关闭客户端连接:
...
Logger.LogInformation("Closing secondary signalR connection...");
await hubConnection.StopAsync();
Logger.LogInformation("Closed secondary signalR connection");
...
The The The
方法显示为阻塞,即没有为
“已关闭辅助signalR连接”
..。虽然,
我的服务器集线器的处理程序显示连接已断开。这类似于
行为
在这篇文章中描述了
问题
..。
如何正确处理signalR连接
ASP.NET核心3.1
完整的代码清单如下:
处理signalR连接
#region Dispose
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Clear secondary signalR Closed event handler and stop the
/// secondary signalR connection
///
///
/// ASP.NET Core Release Candidate 5 calls DisposeAsync when
/// navigating away from a Blazor Server page. Until the
/// release is stable DisposeAsync will have to be triggered from
/// Dispose. Sadly, this means having to use GetAwaiter().GetResult()
/// in Dispose().
/// However, providing DisposeAsync() now makes the migration easier
/// https://github.com/dotnet/aspnetcore/issues/26737
/// https://github.com/dotnet/aspnetcore/issues/9960
/// https://github.com/dotnet/aspnetcore/milestone/57?closed=1
///
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
Logger.LogInformation("Index.razor page is disposing...");
try
{
if (hubConnection != null)
{
Logger.LogInformation("Removing signalR client event handlers...");
hubConnection.Closed -= CloseHandler;
}
// Until ASP.NET Core 5 is released in November
// trigger DisposeAsync(). See docstring and DiposeAsync() below.
// not ideal, but having to use GetAwaiter().GetResult() until
// forthcoming release of ASP.NET Core 5 for the introduction
// of triggering DisposeAsync on pages that implement IAsyncDisposable
DisposeAsync().GetAwaiter().GetResult();
}
catch (Exception exception)
{
Logger.LogError($"Exception encountered while disposing Index.razor page :: {exception.Message}");
}
}
disposed = true;
}
///
/// Dispose the secondary backend signalR connection
///
///
/// ASP.NET Core Release Candidate 5 adds DisposeAsync when
/// navigating away from a Blazor Server page. Until the
/// release is stable DisposeAsync will have to be triggered from
/// Dispose. Sadly, this means having to use GetAwaiter().GetResult()
/// in Dispose().
/// However, providing DisposeAsync() now makes the migration easier
/// https://github.com/dotnet/aspnetcore/issues/26737
/// https://github.com/dotnet/aspnetcore/issues/9960
/// https://github.com/dotnet/aspnetcore/milestone/57?closed=1
///
public async virtual ValueTask DisposeAsync()
{
try
{
if (hubConnection != null)
{
Logger.LogInformation("Closing secondary signalR connection...");
await hubConnection.StopAsync();
Logger.LogInformation("Closed secondary signalR connection");
}
// Dispose(); When migrated to ASP.NET Core 5 let DisposeAsync trigger Dispose
}
catch (Exception exception)
{
Logger.LogInformation($"Exception encountered wwhile stopping secondary signalR connection :: {exception.Message}");
}
}
#endregion
Blazor服务器的完整代码页
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using WebApp.Data;
using WebApp.Data.Serializers.Converters;
using WebApp.Data.Serializers.Converters.Visitors;
using WebApp.Repository.Contracts;
namespace WebApp.Pages
{
public partial class Index : IAsyncDisposable, IDisposable
{
private HubConnection hubConnection;
public bool IsConnected => hubConnection.State == HubConnectionState.Connected;
private bool disposed = false;
[Inject]
public NavigationManager NavigationManager { get; set; }
[Inject]
public IMotionDetectionRepository Repository { get; set; }
[Inject]
public ILogger LoggerMotionDetection { get; set; }
[Inject]
public ILogger LoggerMotionInfo { get; set; }
[Inject]
public ILogger LoggerJsonVisitor { get; set; }
[Inject]
public ILogger Logger { get; set; }
#region Dispose
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Clear secondary signalR Closed event handler and stop the
/// secondary signalR connection
///
///
/// ASP.NET Core Release Candidate 5 calls DisposeAsync when
/// navigating away from a Blazor Server page. Until the
/// release is stable DisposeAsync will have to be triggered from
/// Dispose. Sadly, this means having to use GetAwaiter().GetResult()
/// in Dispose().
/// However, providing DisposeAsync() now makes the migration easier
/// https://github.com/dotnet/aspnetcore/issues/26737
/// https://github.com/dotnet/aspnetcore/issues/9960
/// https://github.com/dotnet/aspnetcore/milestone/57?closed=1
///
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
Logger.LogInformation("Index.razor page is disposing...");
try
{
if (hubConnection != null)
{
Logger.LogInformation("Removing signalR client event handlers...");
hubConnection.Closed -= CloseHandler;
}
// Until ASP.NET Core 5 is released in November
// trigger DisposeAsync(). See docstring and DiposeAsync() below.
// not ideal, but having to use GetAwaiter().GetResult() until
// forthcoming release of ASP.NET Core 5 for the introduction
// of triggering DisposeAsync on pages that implement IAsyncDisposable
DisposeAsync().GetAwaiter().GetResult();
}
catch (Exception exception)
{
Logger.LogError($"Exception encountered while disposing Index.razor page :: {exception.Message}");
}
}
disposed = true;
}
///
/// Dispose the secondary backend signalR connection
///
///
/// ASP.NET Core Release Candidate 5 adds DisposeAsync when
/// navigating away from a Blazor Server page. Until the
/// release is stable DisposeAsync will have to be triggered from
/// Dispose. Sadly, this means having to use GetAwaiter().GetResult()
/// in Dispose().
/// However, providing DisposeAsync() now makes the migration easier
/// https://github.com/dotnet/aspnetcore/issues/26737
/// https://github.com/dotnet/aspnetcore/issues/9960
/// https://github.com/dotnet/aspnetcore/milestone/57?closed=1
///
public async virtual ValueTask DisposeAsync()
{
try
{
if (hubConnection != null)
{
Logger.LogInformation("Closing secondary signalR connection...");
await hubConnection.StopAsync();
Logger.LogInformation("Closed secondary signalR connection");
}
// Dispose(); When migrated to ASP.NET Core 5 let DisposeAsync trigger Dispose
}
catch (Exception exception)
{
Logger.LogInformation($"Exception encountered wwhile stopping secondary signalR connection :: {exception.Message}");
}
}
#endregion
#region ComponentBase
///
/// Connect to the secondary signalR hub after rendering.
/// Perform on the first render.
///
///
/// This could have been performed in OnInitializedAsync but
/// that method gets executed twice when server prerendering is used.
///
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var hubUrl = NavigationManager.BaseUri.TrimEnd('/') + "/motionhub";
try
{
Logger.LogInformation("Index.razor page is performing initial render, connecting to secondary signalR hub");
hubConnection = new HubConnectionBuilder()
.WithUrl(hubUrl)
.ConfigureLogging(logging =>
{
logging.AddConsole();
logging.AddFilter("Microsoft.AspNetCore.SignalR", LogLevel.Information);
})
.AddJsonProtocol(options =>
{
options.PayloadSerializerOptions = JsonConvertersFactory.CreateDefaultJsonConverters(LoggerMotionDetection, LoggerMotionInfo, LoggerJsonVisitor);
})
.Build();
hubConnection.On("ReceiveMotionDetection", ReceiveMessage);
hubConnection.Closed += CloseHandler;
Logger.LogInformation("Starting HubConnection");
await hubConnection.StartAsync();
Logger.LogInformation("Index Razor Page initialised, listening on signalR hub url => " + hubUrl.ToString());
}
catch (Exception e)
{
Logger.LogError(e, "Encountered exception => " + e);
}
}
}
protected override async Task OnInitializedAsync()
{
await Task.CompletedTask;
}
#endregion
#region signalR
/// Log signalR connection closing
///
/// If an exception occurred while closing then this argument describes the exception
/// If the signaR connection was closed intentionally by client or server, then this
/// argument is null
///
private Task CloseHandler(Exception exception)
{
if (exception == null)
{
Logger.LogInformation("signalR client connection closed");
}
else
{
Logger.LogInformation($"signalR client closed due to error => {exception.Message}");
}
return Task.CompletedTask;
}
///
/// Add motion detection notification to repository
///
/// Motion detection received via signalR
private void ReceiveMessage(MotionDetection message)
{
try
{
Logger.LogInformation("Motion detection message received");
Repository.AddItem(message);
StateHasChanged();
}
catch (Exception ex)
{
Logger.LogError(ex, "An exception was encountered => " + ex.ToString());
}
}
#endregion
}
}
signalR服务器集线器
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Logging;
namespace WebApp.Realtime.SignalR
{
///
/// This represents endpoints available on the server, available for the
/// clients to call
///
public class MotionHub : Hub
{
private bool _disposed = false;
public ILogger Logger { get; set; }
public MotionHub(ILogger logger) : base()
{
Logger = logger;
}
public override async Task OnConnectedAsync()
{
Logger.LogInformation($"OnConnectedAsync => Connection ID={Context.ConnectionId} : User={Context.User.Identity.Name}");
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
if (exception != null)
{
Logger.LogInformation($"OnDisconnectedAsync => Connection ID={Context.ConnectionId} : User={Context.User.Identity.Name} : Exception={exception.Message}");
}
else
{
Logger.LogInformation($"OnDisconnectedAsync => Connection ID={Context.ConnectionId} : User={Context.User.Identity.Name}");
}
await base.OnDisconnectedAsync(exception);
}
// Protected implementation of Dispose pattern.
protected override void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
_disposed = true;
// Call base class implementation.
base.Dispose(disposing);
}
}
}
发布于 2020-10-21 13:09:58
在帮助下修复
ASP.NET核心Github讨论
..。
在
方法已替换
这调用
而无需等待任务结果。
我还更新了停止集线器连接的代码:
try { await hubConnection.StopAsync(); }
finally
{
await hubConnection.DisposeAsync();
}
在
the the the
调用
不再阻塞,连接将正常关闭。
https://stackoverflow.com/questions/64449506
复制相似问题