asp.net-coreasp.net-web-apicontrollersignalrsignalr-hub

How to invoke hub`s method in controller? SignalR ASP.NET Core


How can I call/Invoke Hubs method "DisconnectFromConversation" from my API controller

Hubs implementation

    public class ChatHub : Hub
    {
    private readonly IConversationService \_converstaionService;
    private readonly IMemberService \_converstaionUserService;
    private readonly IMessageService \_messageService;
    
            public ChatHub(IConversationService converstaionService,
                           IMemberService converstaionUserService, IMessageService messageService)
            {
                _converstaionService = converstaionService;
                _converstaionUserService = converstaionUserService;
                _messageService = messageService;
            }
        
           
            public async Task DisconnectFromConversation(Guid conversationId, Guid userId)
            {
                if (ConnectionMapping.TryGetValue(userId, out var connectionIds))
                {
                    
                    foreach (var connectionId in connectionIds)
                    {
                        await Groups.RemoveFromGroupAsync(connectionId,   conversationId.ToString());
                    }
                }
            }

Controllers implementation

    public class MemberController : ControllerBase
    {
    private readonly IMemberService \_memberService;
    private readonly IConversationService \_conversationService;
    private readonly IHubContext\<ChatHub\> \_hubContext;
    
            public MemberController(IMemberService memberService, IConversationService       conversationService, IHubContext<ChatHub> hubContext)
            {
                _memberService = memberService;
                _conversationService = conversationService;
                _hubContext = hubContext;
            }
        
            [HttpDelete("conversations/{conversationId}")]
            public async Task<IActionResult> DeleteMember([FromBody]string userId, Guid conversationId)
            {
                var test = Request.Headers["connectionId"];
                if (!Request.Headers.ContainsKey("User-Id"))
                {
                    return BadRequest();
                }
                else if (await _conversationService.IsUserCreator(conversationId,
                     new Guid(Request.Headers["User-Id"]))
                     || Request.Headers["User-Id"] == userId.ToString())
                {
                    await _memberService.DeleteMemberAsync(conversationId, new Guid(userId));
                    //await _hubContext.Clients.All
                        .BrodacastMessage("DisconnectFromConversation", conversationId, userId);
                    await _hubContext.Clients.Client(test)
                        .SendAsync("DisconnectFromConversation", conversationId, userId);
                    return NoContent();
                }
                else
                {
                    return Forbid();
                }
            }
        }

I tried to invoke by next way await _hubContext.Clients.All.BrodacastMessage("DisconnectFromConversation", conversationId, userId); but in debug mode it doesnt trigger breakpoint in hub method I also tried await _hubContext.Clients.Client(test).SendAsync("DisconnectFromConversation", conversationId, userId); I was thinking to delegate this action to client side. But unfortunatly I dont have it (it`s one of the requirement of Task)


Solution

  • Using ...

    await _hubContext.Clients.All.BrodacastMessage("DisconnectFromConversation", conversationId, userId);
    

    will try to call a Client-Side method called "DisconnectFromConversation".

    The IHubContext<T> gives you access to Groups, Clients, etc. for sending messages to client but does not give you access to methods declared on the service-side hub.

    Move the method to a service and inject the IHubContext into that so you can can access the IHubContext Groups, Clients, etc., e.g.:

    public class ChatHubService
    {
        private readonly IHubContext<ChatHub> _hubContext;
    
        public ChatService(IHubContext<ChatHub> hubContext)
        {
            _hubContext = hubContext;
        }
    
        public async Task DisconnectUserFromConversation(Guid conversationId, Guid userId)
        {
            if (ConnectionMapping.TryGetValue(userId, out var connectionIds))
            {
                foreach (var connectionId in connectionIds)
                {
                    await _hubContext.Groups.RemoveFromGroupAsync(connectionId, conversationId.ToString());
                }
            }
        }    
    }
    

    Then inject that service into your server chathub and controller:

    public class ChatHub : Hub
    {
        private readonly ChatHubService _chatHubService;
    
        public ChatHub(ChatHubService chatHubService)
        {
            _chatHubService = chatHubService;
        }
    
        public async Task DisconnectFromConversation(Guid conversationId, Guid userId)
        {
            await _chatHubService.DisconnectUserFromConversation(conversationId, userId);
        }
    }
    

    and

    public class MemberController : ControllerBase
    {
        private readonly ChatHubService _chatHubService;
    
        public MemberController(ChatHubService chatHubService)
        {
            _chatService = chatService;
        }
    
        [HttpDelete("conversations/{conversationId}")]
        public async Task<IActionResult> DeleteMember([FromBody]string userId, Guid conversationId)
        {
            await _chatHubService.DisconnectUserFromConversation(conversationId, new Guid(userId));
        }
    }