从回调调用时,ASP.NET Core SignalR Clients对象已处置异常

Wim*_*uts 5 c# signalr asp.net-core asp.net-core-signalr

我在干净的新ASP.NET Core 2.1 Web应用程序中具有以下中心实现。我刚刚更新到最新版本的ASP.NET Core 2.1和最新版本的Visual Studio 2017。

该类起作用,从调试器启动时,一个客户端连接。我可以在调试器中看到它,并且可以在客户端看到它,因为我记录了连接后发送的“ userobject”。客户端保持连接状态。

在第二步中,我将IPbxConnection注入到集线器中,该集线器也正常工作(我可以在调试器中看到有效的对象)。IPbxConnection实现将在5秒钟后调用OnUserUpdated处理程序(我现在通过IPbxConnection实现中的计时器回调来执行此操作以进行测试)。这总是会导致将对象处置异常抛出到Clients对象上。如何在此回调中向所有客户端发送通知?似乎Clients对象不会保持其状态,并且仅在消息处理程序中有效……但是,我想一直将信息推送到客户端。

public class PresenceHub : Hub
{
    //Members
    private IPbxConnection _connection; 

    /// <summary>
    /// Constructor, pbx connection must be provided by dependency injection
    /// </summary>        
    public PresenceHub(IPbxConnection connection)
    {
        _connection = connection;
        _connection.OnUserUpdated((e) =>
        {                
            Clients.All.SendAsync("UpdateUser", "updateuserobject");
        });
        _connection.Connect();
    }

    /// <summary>
    /// Called whenever a user is connected to the hub. We will send him all the user information
    /// </summary>
    public override async Task OnConnectedAsync()
    {            
        await base.OnConnectedAsync();
        await Clients.Caller.SendAsync("AddUser", "userobject");
    }
}
Run Code Online (Sandbox Code Playgroud)

小智 7

正如 Github https://github.com/aspnet/SignalR/issues/2424上的这个问题所述, 集线器的设计寿命很短,每次调用后都会被丢弃。

我发现在集线器上的当前请求范围之外访问您的客户端的唯一方法是将 HubContext 注入一个单独的类。

在您的情况下,对所有客户的广播看起来像这样

public class HubEventEmitter
{
    private IHubContext<PresenceHub> _hubContext;

    public HubEventEmitter(IPbxConnection connection, IHubContext<PresenceHub> hubContext)
    {
        _hubContext = hubContext;
        _connection.OnUserUpdated((e) =>
        {
            _hubContext.Clients.All.SendAsync("UpdateUser", "updateuserobject");
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您只想通知特定的客户端,则必须从上下文中收集 connectionId 并像这样使用它

_hubContext.Clients.Client(connectionId).SendAsync("UpdateUser", "updateuserobject");
Run Code Online (Sandbox Code Playgroud)


小智 7

这个简单的解决方案对我有用。我没有在启动类中添加任何额外的内容。

编辑经过一番思考,我决定虽然这段代码可以工作,但这不是一个好的模式,尤其是因为静态代码最终尝试使用已处置对象中的字段。集线器是一种轻量级、寿命短的容器,应该这样使用。因此,我将长时间运行的流程从 Hub 的静态元素转移到 IHostedService 模式

我的集线器包含在静态成员中定义的长时间运行的异步进程。由于集线器是暂时的,因此在某些情况下,当异步进程尝试发送消息时,集线器将被释放。我添加了一个集线器上下文以注入集线器构造函数

public class IisLogFileHub : Hub
{
    IHubContext<IisLogFileHub> _hubContext = null;

    public IisLogFileHub(IHubContext<IisLogFileHub> hubContext)
    {
        _hubContext = hubContext;
    }
}
Run Code Online (Sandbox Code Playgroud)

在长时间运行的流程中的任何一点,都可以按如下方式发送消息

await _hubContext.Clients.All.SendAsync("ReceiveMessage", msg);
Run Code Online (Sandbox Code Playgroud)


Wim*_*uts 0

github https://github.com/aspnet/Docs/issues/6888上的这个问题隐含地给出了答案。这是文档中的一个“问题”,所以我尝试做的事情肯定是不可能的(但是它没有提到如何做到这一点)。

似乎整个集线器类实例仅在进行调用时才存在,因此您无法使用回调。