C
C#•3w ago
surwren

Data Retrieval in a SignalR Hub/Service?

I am used to repo -> service -> controller MVC models, and I do not have much experience with in-memory retrieval beyond using LinQ. Say I have a class which could have multiple queryable fields:
public string SignalRId { get; set; }
public int UserId { get; set; }
public string Nickname { get; set; }
public string Region { get; set; }
public string RoomName { get; set; }
public string SignalRId { get; set; }
public int UserId { get; set; }
public string Nickname { get; set; }
public string Region { get; set; }
public string RoomName { get; set; }
In a normal appcontextdb access scenario, I would just pull out the values using EFCore LinQ using something like
var user = await _context.Users.FirstOrDefaultAsync(u => u.UserId == UserId)
var user = await _context.Users.FirstOrDefaultAsync(u => u.UserId == UserId)
In my SignalR Hub, I am using an in-memory method and not SQL to store my data since I don't expect users to have long-lasting data (unlikely they will maintain a connection for >5minutes per session). Therefore I inject a ConnectedClientsService and don't have a DB table. - Is using FirstOrDefaultAsync with regular IQueryable is more standard practice in this case (for example, searching for strings matching region, etc)? - Is it standard practice to use 2 concurrent dictionaries with a lock to enable O(1) access?
4 Replies
surwren
surwrenOP•3w ago
For reference, this is my code:
public class ConnectedClientsService : IConnectedClientsService {
private readonly ConcurrentDictionary<int, CMPPlayerDto> _clientsByUserId;
private readonly ConcurrentDictionary<string, CMPPlayerDto> _clientsByConnectionId;
private readonly object _lock = new object();
...
}
public class ConnectedClientsService : IConnectedClientsService {
private readonly ConcurrentDictionary<int, CMPPlayerDto> _clientsByUserId;
private readonly ConcurrentDictionary<string, CMPPlayerDto> _clientsByConnectionId;
private readonly object _lock = new object();
...
}
public class RoomHub : Hub {
private readonly IConnectedClientsService _connectedClientsService;

public RoomHub(IConnectedClientsService connectedClientsService) {
_connectedClientsService = connectedClientsService;
}

public async Task JoinRoom(int userId, string region, string roomName) {
CMPPlayerDto newPlayer = await CreateCMPPlayer(Context.ConnectionId, userId, region, roomName);
if (_connectedClientsService.AddClient(newPlayer)) {
await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
await Clients.All.SendAsync("UserJoinedRoom", newPlayer);
}
else {
...
}
}

}
public class RoomHub : Hub {
private readonly IConnectedClientsService _connectedClientsService;

public RoomHub(IConnectedClientsService connectedClientsService) {
_connectedClientsService = connectedClientsService;
}

public async Task JoinRoom(int userId, string region, string roomName) {
CMPPlayerDto newPlayer = await CreateCMPPlayer(Context.ConnectionId, userId, region, roomName);
if (_connectedClientsService.AddClient(newPlayer)) {
await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
await Clients.All.SendAsync("UserJoinedRoom", newPlayer);
}
else {
...
}
}

}
Imtiaz
Imtiaz•3w ago
I believe, for the concurrent dictionary, you don't need a lock object as it is thread safe. Also, FirstOrDefault is fine, but make sure you register IConnectedClientsService as a singleton not scoped or transient.
Unknown User
Unknown User•3w ago
Message Not Public
Sign In & Join Server To View
surwren
surwrenOP•3w ago
The lock was to make modifying both dictionaries atomic, is it unnecessary in that case? 😳 I completely did not know this lmao I need to read up

Did you find this page helpful?