我的Blazor WebAssembly应用程序中有一个SignalRHubConnection
,虽然它大部分时间都能工作,但如果我重新加载页面(通过浏览器重新加载(,那么我经常在控制台中收到以下错误,并且无法连接:
未捕获错误:正在调用的委托目标不再可用。请检查是否已提前GC。位于Object.invoke_delegate(dotnet.5.04.js:1(位于WebSocket。(dotnet.5.04.js:1(
以下是我创建HubConnection
(并处理它(的代码的粗略简化视图。
@inherits LayoutBase
@attribute [Authorize]
<AuthorizeView>
<Authorized>
//...
</Authorized>
<NotAuthorized>
//...
</NotAuthorized>
</AuthorizeView>
public class LayoutBase : LayoutComponentBase, IAsyncDisposable
{
[Inject] public IAccessTokenProvider AccessTokenProvider { get; set; }
private readonly HubConnection _hubConnection;
protected override async Task OnInitializedAsync()
{
_hubConnection = new HubConnectionBuilder()
.AddNewtonsoftJsonProtocol(c =>
{
//...
})
.WithUrl(notificationHubUrl, option => option.AccessTokenProvider = GetAccessToken)
.WithAutomaticReconnect()
.Build();
_hubConnection.Closed += HubConnectionOnClosed;
_hubConnection.Reconnected += HubConnectionOnReconnected;
_hubConnection.Reconnecting += HubConnectionOnReconnecting;
await _hubConnection.StartAsync()
await base.OnInitializedAsync();
}
private async Task<string> GetAccessToken()
{
var tokenResult = await AccessTokenProvider.RequestAccessToken(...)
// etc...
}
// .. Event Handlers
public ValueTask DisposeAsync()
{
_logger.LogInformation($"Disposing Hub: {_hubConnection.ConnectionId}");
_hubConnection.Closed -= HubConnectionOnClosed;
_hubConnection.Reconnected -= HubConnectionOnReconnected;
_hubConnection.Reconnecting -= HubConnectionOnReconnecting;
return _hubConnection.DisposeAsync();
}
}
以前我把它作为一个注入的服务,但我最终把它简化为这个结构,但它在重新加载时仍然会出现这个错误。不是每次我都重新加载,而是大多数时候。
我尝试过更改处置模式,但没有成功。我在其他地方找不到关于这个错误的任何信息。
有什么想法吗?
我对根本原因没有明确的答案,但我怀疑这是SignalR/dotnet框架中的某个错误,导致对委托进行GCing,因为有东西删除了对它的引用。
我设法合理一致地引发这个错误的一种方法是让一个处理程序只返回一个Task
,例如
_hubConnection.On<TEvent>(eventType.Name, OnEvent);
OnEvent如下所示:
// THIS IS THE BROKEN SIGNATURE - DO NOT USE
private async Task OnEvent<TEvent>(TEvent e)
{
}
出现的一个解决方法是让处理程序实际返回一些东西。这似乎使框架中更深层次的东西拥有更长时间的引用,这样它就不会获得GC’ed。例如
// WORKS ON MY MACHINE - Note the return type of Task<object>
private async Task<object> OnEvent<TEvent>(TEvent e)
{
// ... Do stuff
return null;
}