首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >关闭/断开ASP.NET核心signalR客户端连接的正确方法是什么?

关闭/断开ASP.NET核心signalR客户端连接的正确方法是什么?
EN

Stack Overflow用户
提问于 2020-10-20 16:33:40
回答 1查看 3.2K关注 0票数 2

我是一个新用户,正在努力从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连接,请执行以下操作。我使用以下代码关闭客户端连接:

代码语言:javascript
运行
AI代码解释
复制
...
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连接

代码语言:javascript
运行
AI代码解释
复制
#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服务器的完整代码页

代码语言:javascript
运行
AI代码解释
复制
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服务器集线器

代码语言:javascript
运行
AI代码解释
复制
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);
        }
    }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-10-21 13:09:58

在帮助下修复

ASP.NET核心Github讨论

..。

方法已替换

这调用

而无需等待任务结果。

我还更新了停止集线器连接的代码:

代码语言:javascript
运行
AI代码解释
复制
try { await hubConnection.StopAsync(); }
  finally
  {
    await hubConnection.DisposeAsync();
  }

the the the

调用

不再阻塞,连接将正常关闭。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64449506

复制
相关文章
如何为程序包创建自己的存储库?
对于Linux,分发软件的最常见方法是rpm或deb格式的二进制软件包。大多数软件包都包含在官方发行版本存储库或第三方软件存储库中。但是,在某些情况下,您只需要安装几个独立的软件包即可。您也许可以使用本地软件包安装工具,即dpkg或rpm,但是在某些情况下,由于依赖关系项而无法安装软件包,因此您需要手动安装所有依赖项。这可能需要一些时间,而且不太容易。但是有一个解决方案可以提供帮助,您可以创建自己的本地存储库,然后将软件包部署到该本地存储库。
用户6543014
2020/01/16
2K0
如何创建多进程程序?(文末福利)
在《对进程和线程的一些总结》已经介绍了进程和线程的区别,但是在C/C++中如何创建进程呢?或者说如何编写多进程的程序呢?
编程珠玑
2019/08/19
1.7K0
vs2017 C#程序打包安装部署之创建Windows安装项目
http://www.360doc.com/content/19/0519/10/59918970_836667460.shtml
zls365
2020/08/19
2K0
vs2017 C#程序打包安装部署之创建Windows安装项目
你知道何为线程与进程吗??
什么是线程?线程与进程与有什么关系?这是一个非常抽象的问题,也是一个特别广的话题,涉及到非常多的知识。我不能确保能把它讲的话,也不能确保讲的内容全部都正确。即使这样,我也希望尽可能地把他讲通俗一点,讲的明白一点,因为这是个一直困扰我很久的,扑朔迷离的知识领域,希望通过我的理解揭开它一层一层神秘的面纱。
刘盼
2018/07/26
8850
你知道何为线程与进程吗??
你知道何为线程与进程吗??
什么是线程?线程与进程与有什么关系?这是一个非常抽象的问题,也是一个特别广的话题,涉及到非常多的知识。我不能确保能把它讲的话,也不能确保讲的内容全部都正确。即使这样,我也希望尽可能地把他讲通俗一点,讲的明白一点,因为这是个一直困扰我很久的,扑朔迷离的知识领域,希望通过我的理解揭开它一层一层神秘的面纱。
Java高级架构
2018/10/22
5070
你知道何为线程与进程吗??
如何为项目配置opencv
[在这里插入图片描述] [在这里插入图片描述] [在这里插入图片描述] 配置: 包含目录: D:\OpenCV\opencv\build\include D:\OpenCV\opencv\build\include\opencv2 库目录: D:\OpenCV\opencv\build\x64\vc15\lib [在这里插入图片描述] 输入: 这两个在opencv\build\x64\vc15\lib文件下 opencv_world411.lib opencv_world411d.lib [在这里插入图片描
陶陶name
2022/05/12
4480
创建SpringBoot自动配置:自定义Starter项目
创建SpringBoot自动配置项目 经过前面章节的学习,我们已经了解了 Spring Boot 的核心运作原理,同时也学习了几个常用框架的自动配置机制及源代码解析。Spring Boot 默认实现
愿天堂没有BUG
2022/10/28
6150
创建SpringBoot自动配置:自定义Starter项目
如何为Joomla标签创建布局覆盖
这将在/templates/your-template/html/layouts/joomla/content/文件夹中生成一组文件。
用户3850506
2019/07/30
1.5K0
VS2017 创建&安装项目模板
当你想使用VS做一道OI竞赛题目时,你的步骤是: 新建空项目 | 添加源文件 | 重命名源文件 | 双击打开源文件 写上万年不变的include、main、return 0, 真是如此的繁琐; 且还不方便添加日期时间等信息
战神伽罗
2019/07/24
1.5K0
ubuntu、deepin 自定义程序启动菜单创建
版权声明:本文为 码农笔录 公众号 原创文章,未经博主允许不得转载。 https://blog.csdn.net/yp090416/article/details/88692054
码农笔录
2019/04/09
1.4K0
ubuntu、deepin 自定义程序启动菜单创建
安装pycharm创建新项目时出现错误interpreter field is empty,运行python程序
2.安装python,具体步骤参考如下博客的Python的安装部分,记住安装路径:
拓荒者
2019/03/11
4.1K0
安装pycharm创建新项目时出现错误interpreter field is empty,运行python程序
TensorFlow初学者指南:如何为机器学习项目创建合适的文件架构
选自MetaFlow 作者:Morgan 机器之心编译 参与:李亚洲、蒋思源 在这篇文章中,作者根据自己的经验为 TensorFlow 初学者给出了设计文件、文件夹架构的建议。在管理自己的项目时,这会是非常有帮助的。 在机器学习中,设计正确的文件架构并不简单。我自己在几个项目上纠结过此问题之后,我开始寻找简单的模式,并希望其能覆盖大部分在读代码或自己编代码时遇到的使用案例。 在此文章中,我会分享我自己的发现。 声明:该文章更像是建议,而非明确的指导,但我感觉挺成功的。该文章意在为初学者提供起点,可能会引发一
机器之心
2018/05/07
7100
TensorFlow初学者指南:如何为机器学习项目创建合适的文件架构
如何为Hue添加自定义Banner
在用户使用Hue时,如果需要在Hue界面设置一些提示等信息(如:“Hue用户申请及权限放通,请联系xxx”)时。那这是我们可以通过配置在Hue界面添加该提示信息。
Fayson
2018/10/23
8880
如何为Hue添加自定义Banner
systemd创建守护进程
登录服务器,vim /etc/systemd/system/test.service 复制以下文件: [Unit] Description=my-test # 在网络初始化之后启动 #After=network.target [Service] # 服务类型 Type=simple # 进程退出立即重启 Restart=always #设置所属的用户和用户组,可选 User=crocodile Group=crocodile # 启动命令 ExecStart=/bin/bash /usr/local/
槽痞
2020/06/23
2.4K0
Linux - fork() 创建进程
Stack - 所有函数的 local variables, arguments 和 return address 的存放内存区域
Spell Thief
2020/10/30
5.8K0
Linux - fork() 创建进程
Scrapy库安装和项目创建建议收藏
  使用pip命令安装scrapy,在安装过程中可能会因为缺少依赖库而报错,根据报错提示依次下载需要的依赖库,下载过程中注意系统类型和Python版本
全栈程序员站长
2022/07/14
4630
【Linux修炼】11.进程的创建、终止、等待、程序替换
在linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
每天都要进步呀
2023/03/28
6.2K0
【Linux修炼】11.进程的创建、终止、等待、程序替换
Linux系统编程-进程创建(fork)、外部程序调用(exec)
在linux中fork函数是非常重要的函数,它可以从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
DS小龙哥
2022/02/17
3.1K0
Linux系统编程-进程创建(fork)、外部程序调用(exec)
如何为Nginx创建自签名SSL证书
SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。 TLS与SSL在传输层对网络连接进行加密。
尘埃
2018/07/20
11.9K0
点击加载更多

相似问题

布尔查询格式不正确,应为END_OBJECT,但找到了FIELD_NAME

10

布尔查询格式错误,应为END_OBJECT,但发现FIELD_NAME无法查询_search

110

查询格式不正确,应为[END_OBJECT],但找到了[FIELD_NAME]

221

搜索查询[匹配]格式错误的查询时出错,应为[END_OBJECT],但找到了[FIELD_NAME]

194

Elasticsearch提供了[function_score]格式错误的查询,应为[END_OBJECT],但找到了[FIELD_NAME]

1104
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档