Search code examples
c#signalrsignalr-hub

SignalR hub Clients object disposed


I have the following SignalR Hub with in-memory connection management.

[Authorize]
public class ChatHub : Hub
{
    private static readonly ConnectionMapping<string> _connections =
        new ConnectionMapping<string>();

    private readonly IMessageRepository _messagesRepository;

    public ChatHub(IMessageRepository messageRepository)
    {
        _messagesRepository = messagesRepository;
    }

    public override async Task OnConnectedAsync()
    {
        var name = Context.User.Identity.Name;
        _connections.Add(name, Context.ConnectionId);

        //await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception exception)
    {
        var name = Context.User.Identity.Name;
        _connections.Remove(name, Context.ConnectionId);

        //await Groups.RemoveFromGroupAsync(Context.ConnectionId, "SignalR Users");
        await base.OnDisconnectedAsync(exception);
    }

    public async void SendMessage(ChatMessage chatMessage)
    {
        var name = Context.User.Identity.Name;

        var chatHistoryMessage = await _messagesRepository.SaveMessageAsync(chatMessage);

        var availableConnections = _connections.GetConnections(name);

        if (availableConnections.Any())
        {
            foreach (var connectionId in availableConnections)
            {
                Clients.Client(connectionId).SendAsync("ReceiveMessage", chatHistoryMessage);
            }
        }
        else
        {
        }
    }
}

However, when executing the code the following line

Clients.Client(connectionId).SendAsync("ReceiveMessage", 1);

raises an object disposed error on Clients.

This issue started happening when I added the repository line:

var chatHistoryMessage = await _messagesRepository.SaveMessageAsync(chatMessage);

SaveMessageAsync method:

public async Task<ChatHistory> SaveMessageAsync (ChatMessage chatMessage)
        {
            using (var conn = new SqlConnection(ConnProvider.ConnectionString))
            {
                await conn.OpenAsync();
                return (await conn.QueryAsync<ChatHistory>("[mob].[spSaveChatMessage]",
                    new
                    {
                       ..
                    },
                    commandType: CommandType.StoredProcedure)).FirstOrDefault();
            }
        }

Why would my Clients object be disposed? If I wait with the debugger that issue never happens.


Solution

  • It looks like the SendMessage method should be an async Task rather than an async void.

    The issue could be caused by the way the SignalR framework runs async voids.

    See this article for a good overview.

    Async methods returning void don’t provide an easy way to notify the calling code that they’ve completed.