Search code examples
asp.net-web-apisignalrsignalr-hubsignalr.clientasp.net-core-signalr

how to implement groups in signalR in ASP.net core web application


I am using signalR groups for, whenever a user changed a data then every user in the group can see the changed data in the real time.

I have used ASP.net core web application tempelate for this project.

the code for the controller is:

public class PatientCollectionsController : ControllerBase
{
    private readonly IHubContext<GroupHub> groupHub;

    public PatientCollectionsController(Func<string, IPatientCollectionsService> serviceResolver, IConfiguration configuration, ILogger<PatientCollectionsModel> logger,
        IHubContext<GroupHub> grpHub)
    {
        this.dbservice = serviceResolver(configuration.GetValue<string>("CollectionsDbChoice").ToLower());
        this.logger = logger;
        this.groupHub = grpHub;
    }

    // GET: api/<PatientCollectionsController>
    [HttpGet]
    public IActionResult Get()
    {
        HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);

        try
        {
            return Ok(dbservice.GetPatientCollections());
        }
        catch (Exception ex)
        {
            logger.LogError("failed to get Patient Collections", ex.Message, ex.StackTrace);
            logger.LogInformation(ex.Message, ex.StackTrace);
            return StatusCode(500, "Internal server error");
        }
    }
    
    // PUT api/<PatientCollectionsController>/5
    [HttpPut("{id}")]
    public void Put(string id, [FromBody] PatientCollectionsModel value)
    {
        try
        { 
            dbservice.PutPatientCollection(value);
        }
        catch(Exception ex)
        {
            logger.LogError("failed to put Patient Collections", ex.Message, ex.StackTrace);
            logger.LogInformation(ex.Message, ex.StackTrace);
            StatusCode(500, "Internal server error");
        }
    }


    static readonly string[] scopeRequiredByApi = new string[] { "access_as_signedin_user" };
    private IPatientCollectionsService dbservice;
    private ILogger<PatientCollectionsModel> logger;
}

the code for the service is:

public class PatientCollectionsDBService : IPatientCollectionsService
{
    #region ctor
    public PatientCollectionsDBService(IDatabaseSettings settings)
    {
        CloudStorageAccount storageAccount = CloudStorageAccount.Parse(settings.ConnectionString);
        CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
        patientCollectionsTable = tableClient.GetTableReference("PatientCollections");
        patientCollectionsTable.CreateIfNotExists();
    }
    #endregion

    #region IPatientCollectionsService Impl
    public List<PatientCollectionsModel> GetPatientCollections()
    {
        TableContinuationToken token = null;
        var pc = new List<PatientCollectionsModel>();
        do
        {
            var tableQuery = new TableQuery<DynamicTableEntity>
            {
                FilterString = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "current")
            };
            var queryResult = patientCollectionsTable.ExecuteQuerySegmented(tableQuery, token);
            foreach (var entity in queryResult)
            {
                pc.Add(new PatientCollectionsModel
                {
                    Id = entity.RowKey,
                    FirstName = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.FirstName)]),
                    PtId = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.PtId)]),
                    DOS = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.DOS)]),
                    PatientBalance = (float)entity.Properties[nameof(PatientCollectionsModel.PatientBalance)].DoubleValue,
                    EOB = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.EOB)]),
                    PhoneNo = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.PhoneNo)]),
                    BalanceCurrent = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.BalanceCurrent)]),
                    BalanceThirtyPlus = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.BalanceThirtyPlus)]),
                    BalanceThirtyPlusCall = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.BalanceThirtyPlusCall)]),
                    BalanceSixtyPlus = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.BalanceSixtyPlus)]),
                    BalanceSixtyPlusCall = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.BalanceSixtyPlusCall)]),
                    Notes = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.Notes)]),
                    BalanceNinetyPlus = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.BalanceNinetyPlus)]),
                    CollectionOneTwentyPlus = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.CollectionOneTwentyPlus)]),
                    CollectionOneTwentyPlusCall = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.CollectionOneTwentyPlusCall)]),
                    Notes2 = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.Notes2)]),
                    Notes3 = Convert.ToString(entity.Properties[nameof(PatientCollectionsModel.Notes3)])
                });
            }

        } while (token != null);

        return pc;
    }

    public async Task<bool> PutPatientCollection(PatientCollectionsModel pc)
    {
        DynamicTableEntity newOne = new DynamicTableEntity { PartitionKey = "current", RowKey = pc.PtId };
        Dictionary<string, EntityProperty> props = new Dictionary<string, EntityProperty>()
        {
            {nameof(PatientCollectionsModel.FirstName), new EntityProperty(pc.FirstName) },
            {nameof(PatientCollectionsModel.PtId), new EntityProperty(pc.PtId) },
            {nameof(PatientCollectionsModel.PatientBalance), new EntityProperty(pc.PatientBalance) },
            {nameof(PatientCollectionsModel.EOB), new EntityProperty(pc.EOB) },
            {nameof(PatientCollectionsModel.PhoneNo), new EntityProperty(pc.PhoneNo) },
            {nameof(PatientCollectionsModel.BalanceCurrent), new EntityProperty(pc.BalanceCurrent) },
            {nameof(PatientCollectionsModel.BalanceThirtyPlus), new EntityProperty(pc.BalanceThirtyPlus) },
            {nameof(PatientCollectionsModel.BalanceThirtyPlusCall), new EntityProperty(pc.BalanceThirtyPlusCall) },
            {nameof(PatientCollectionsModel.DOS), new EntityProperty(pc.DOS) },
            {nameof(PatientCollectionsModel.BalanceSixtyPlus), new EntityProperty(pc.BalanceSixtyPlus) },
            {nameof(PatientCollectionsModel.BalanceSixtyPlusCall), new EntityProperty(pc.BalanceSixtyPlusCall) },
            {nameof(PatientCollectionsModel.Notes), new EntityProperty(pc.Notes) },
            {nameof(PatientCollectionsModel.BalanceNinetyPlus), new EntityProperty(pc.BalanceNinetyPlus) },
            {nameof(PatientCollectionsModel.CollectionOneTwentyPlus), new EntityProperty(pc.CollectionOneTwentyPlus) },
            {nameof(PatientCollectionsModel.CollectionOneTwentyPlusCall), new EntityProperty(pc.CollectionOneTwentyPlusCall) },
            {nameof(PatientCollectionsModel.Notes2), new EntityProperty(pc.Notes2) },
            {nameof(PatientCollectionsModel.Notes3), new EntityProperty(pc.Notes3) }
        };
        newOne.Properties = props;
        newOne.ETag = "*";

        TableOperation updateDenial = TableOperation.Replace(newOne);
        var tableResult = await patientCollectionsTable.ExecuteAsync(updateDenial);

        return tableResult.HttpStatusCode == (int)HttpStatusCode.OK
            || tableResult.HttpStatusCode == (int)HttpStatusCode.NoContent;
    }

then i created a hub :

[Authorize]
public class GroupHub : Hub
{
    public async Task JoinGroup(string group)
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, group);
        await Clients.Group(group).SendAsync("Send", $"{Context.ConnectionId} has joined the group {group}.");
    }

    public async Task LeaveGroup(string group)
    {
        await Groups.RemoveFromGroupAsync(Context.ConnectionId, group);
    }

}

Can anyone suggest me what should i do to make it work and also how can i send a users to different groups.


Solution

  • await Groups.AddToGroupAsync(Context.ConnectionId, group);
    await Groups.RemoveFromGroupAsync(Context.ConnectionId, group);
    

    As we all known, the connections are added to or removed from groups via the AddToGroupAsync and RemoveFromGroupAsync methods. So, to manage (add/remove) Users in the groups, first you should get the User's ConnectionID.

    In the Hub's OnConnectedAsync method, you could get the ConnectionID via the Context. Then, store the ConnectionID and UserName into the database.

    In the Hub's OnDisconnectedAsync method, if user disconnected, remove the user from the table.

    After that, you could according the UserName to find the ConnectionID and use the AddToGroupAsync and RemoveFromGroupAsync methods to add/remove user to the group.

    //require using Microsoft.AspNetCore.SignalR;
    //require using Microsoft.AspNetCore.Authorization;
    [Authorize]
    public class ChatHub : Hub
    { 
        private readonly ApplicationDbContext _context; //DB context
    
        public ChatHub(ApplicationDbContext context)
        {
            _context = context;
        }
        public override Task OnConnectedAsync()
        { 
            //get Logged user name.
            var IdentityName = Context.User.Identity.Name;
    
            SignalRUser user = new SignalRUser() { ConnectionID = Context.ConnectionId, UserName = IdentityName };
            _context.SignalRUser.Add(user);
            _context.SaveChanges();
    
            return base.OnConnectedAsync();
        } 
        public override async Task OnDisconnectedAsync(Exception exception)
        {
            var IdentityName = Context.User.Identity.Name; 
            var user =  _context.SignalRUser.Where(c => c.UserName == IdentityName).FirstOrDefault();
            //remove user if user disconnected
            if(user != null)
            {
                _context.SignalRUser.Remove(user);
                _context.SaveChanges();
            }
    
            await base.OnDisconnectedAsync(exception);
        }
    }