Search code examples
javascriptjqueryasp.net-mvcasp.net-coresignalr

SignalR updates notification after a forced page refresh


I use signalR in my ASP.NET Core MVC project to push notification to users. I studied the similar question here but its solution did not solve my problem. I have used the following Javascript/jQuery references:

<script type="text/javascript" src="../../js/UserPage/jquery-3.6.0.min.js"></script>
<script src="~/microsoft/signalr/dist/browser/signalr.js"></script>
<script src="~/js/message.js"></script>

message.js is as follows:

var connection = new signalR.HubConnectionBuilder().withUrl("/Home/Messages").withAutomaticReconnect().build();
connection.start();

if (document.getElementById("sendBtn") != null) {
    document.getElementById("sendBtn").addEventListener("click", function () {
        var costCenter = $("#cost-center").val();
        connection.invoke("SendMessage", costCenter).catch(function (err) {
            return console.error(err);
        });
    });
}

the code for hub:

public class MessageHub : Hub
{
    //Dependency injections
    private readonly IUserRepository _userRepository;
    private readonly ICostCenterRepository _costcenterRepository;
    private readonly IHttpContextAccessor _httpContext;

    public MessageHub(IUserRepository userRepository, ICostCenterRepository costcenterRepository, IHttpContextAccessor httpContext)
    {
        _userRepository = userRepository;
        _costcenterRepository = costcenterRepository;
        _httpContext = httpContext;
    }
    public async Task SendMessage(string costCenter)
    {
        var HttpContext = _httpContext.HttpContext;
        string userId = _userRepository.GetUserIdByCostCenter(costCenter).ToString();
        string sender = HttpContext.Session.GetString("department");
        await Clients.User(userId).SendAsync("ReceiveMessage", sender);
    }
}

And, javascript code in the main page is:

<script>
    $(document).ready(function(){
            connection.on("ReceiveMessage", function (param) {
            if($('#badge-count').length){
                var currentMessageNum = parseInt($('#badge-count').text());
                $('#badge-count').text(currentMessageNum + 1);
                $('.main-msg-list').prepend('<li><a class="dropdown-item message-item" style="font-family: vazir !important" asp-controller="Messages" asp-action="Index" href="" id="msg-'+ currentMessageNum + 1 +'">پیام جدید از '+ param +'</a></li>');
            }else{
                $('#badge-count').text('1');
            }
        });
    });
</script>

The problem is that the notification updates after a forced refresh. After that, the notification works fine and updates instantly. How can I fix it?


Solution

  • I fixed this problem by making some changes in the Hub code:

    [Authorize]
    public class MessageHub : Hub
    {
        //Dependancy injections
        private readonly IUserRepository _userRepository;
        private readonly ICostCenterRepository _costcenterRepository;
        private readonly IHttpContextAccessor _httpContext;
    
        public MessageHub(IUserRepository userRepository, ICostCenterRepository costcenterRepository, IHttpContextAccessor httpContext)
        {
            _userRepository = userRepository;
            _costcenterRepository = costcenterRepository;
            _httpContext = httpContext;
        }
        //define a dictionary to store the userid.
        private static Dictionary<string, List<string>> NtoIdMappingTable = new Dictionary<string, List<string>>();
    
        public override async Task OnConnectedAsync()
        {
            var username = Context.User.Identity.Name;
            var userId = Context.UserIdentifier;
            List<string> userIds;
    
            //store the userid to the list.
            if (!NtoIdMappingTable.TryGetValue(username, out userIds))
            {
                userIds = new List<string>();
                userIds.Add(userId);
    
                NtoIdMappingTable.Add(username, userIds);
            }
            else
            {
                userIds.Add(userId);
            }
    
            await base.OnConnectedAsync();
        }
    
        public override async Task OnDisconnectedAsync(Exception exception)
        {
            var username = Context.User.Identity.Name;
    
            //remove userid from the List
            if (NtoIdMappingTable.ContainsKey(username))
            {
                NtoIdMappingTable.Remove(username);
            }
            await base.OnDisconnectedAsync(exception);
        }
        public async Task SendMessage(string costCenter)
        {
            var HttpContext = _httpContext.HttpContext;
            string username = _userRepository.GetUsernameByCostCenter(costCenter).ToString();
            var userId = NtoIdMappingTable.GetValueOrDefault(username);
            string sender = HttpContext.Session.GetString("department");
            await Clients.User(userId.FirstOrDefault()).SendAsync("ReceiveMessage", sender);
        }
    }