如何在 ASP.net 核心 Web 应用程序的 signalR 中实现组

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

我正在使用 signalR 组,每当用户更改数据时,组中的每个用户都可以实时看到更改的数据。

我已经为这个项目使用了 ASP.net 核心 Web 应用程序模板。

控制器的代码是:

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;
}

服务代码是:

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;
    }

然后我创建了一个中心 :

[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);
    }

}

任何人都可以建议我应该怎么做才能使其正常工作以及如何将用户发送到不同的组。

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

众所周知,通过 AddToGroupAsyncRemoveFromGroupAsync 方法将连接添加到组或从组中删除。因此,要管理组中的 (add/remove) 个用户,首先您应该获取用户的 ConnectionID.

在 Hub 的 OnConnectedAsync 方法中,您可以通过 Context 获取 ConnectionID。然后,将 ConnectionIDUserName 存储到数据库中。

在集线器的 OnDisconnectedAsync 方法中,如果用户断开连接,则从 table 中删除用户。

之后,你可以根据UserName找到ConnectionID并使用AddToGroupAsyncRemoveFromGroupAsync方法将add/remove用户加入组.

//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);
    }
}