Solución:
Cuando un cliente invoca una función en el lado del servidor, puede recuperar su ID de conexión a través de Context.ConnectionId
. Ahora, si desea acceder a esa identificación de conexión a través de un mecanismo fuera de un concentrador, puede:
- Simplemente haga que el Hub invoque su método externo pasando el ID de conexión.
- Administre una lista de clientes conectados, también conocidos como
public static ConcurrentDictionary<string, MyUserType>...
agregando al diccionario enOnConnected
y quitando de ella enOnDisconnected
. Una vez que tenga su lista de usuarios, puede consultarla a través de su mecanismo externo.
Ej 1:
public class MyHub : Hub
{
public void AHubMethod(string message)
{
MyExternalSingleton.InvokeAMethod(Context.ConnectionId); // Send the current clients connection id to your external service
}
}
Ej 2:
public class MyHub : Hub
{
public static ConcurrentDictionary<string, MyUserType> MyUsers = new ConcurrentDictionary<string, MyUserType>();
public override Task OnConnected()
{
MyUsers.TryAdd(Context.ConnectionId, new MyUserType() { ConnectionId = Context.ConnectionId });
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
MyUserType garbage;
MyUsers.TryRemove(Context.ConnectionId, out garbage);
return base.OnDisconnected(stopCalled);
}
public void PushData(){
//Values is copy-on-read but Clients.Clients expects IList, hence ToList()
Clients.Clients(MyUsers.Keys.ToList()).ClientBoundEvent(data);
}
}
public class MyUserType
{
public string ConnectionId { get; set; }
// Can have whatever you want here
}
// Your external procedure then has access to all users via MyHub.MyUsers
¡Espero que esto ayude!
La respuesta de Taylor funciona, sin embargo, no tiene en cuenta una situación en la que un usuario tiene varias pestañas del navegador web abiertas y, por lo tanto, tiene varias ID de conexión diferentes.
Para solucionarlo, creé un Diccionario concurrente donde la clave del diccionario es un nombre de usuario y el valor de cada clave es una Lista de conexiones actuales para ese usuario dado.
public static ConcurrentDictionary<string, List<string>> ConnectedUsers = new ConcurrentDictionary<string, List<string>>();
On Connected: agregando una conexión al diccionario de caché global:
public override Task OnConnected()
{
Trace.TraceInformation("MapHub started. ID: {0}", Context.ConnectionId);
var userName = "testUserName1"; // or get it from Context.User.Identity.Name;
// Try to get a List of existing user connections from the cache
List<string> existingUserConnectionIds;
ConnectedUsers.TryGetValue(userName, out existingUserConnectionIds);
// happens on the very first connection from the user
if(existingUserConnectionIds == null)
{
existingUserConnectionIds = new List<string>();
}
// First add to a List of existing user connections (i.e. multiple web browser tabs)
existingUserConnectionIds.Add(Context.ConnectionId);
// Add to the global dictionary of connected users
ConnectedUsers.TryAdd(userName, existingUserConnectionIds);
return base.OnConnected();
}
Al desconectar (cerrar la pestaña): eliminar una conexión del diccionario de caché global:
public override Task OnDisconnected(bool stopCalled)
{
var userName = Context.User.Identity.Name;
List<string> existingUserConnectionIds;
ConnectedUsers.TryGetValue(userName, out existingUserConnectionIds);
// remove the connection id from the List
existingUserConnectionIds.Remove(Context.ConnectionId);
// If there are no connection ids in the List, delete the user from the global cache (ConnectedUsers).
if(existingUserConnectionIds.Count == 0)
{
// if there are no connections for the user,
// just delete the userName key from the ConnectedUsers concurent dictionary
List<string> garbage; // to be collected by the Garbage Collector
ConnectedUsers.TryRemove(userName, out garbage);
}
return base.OnDisconnected(stopCalled);
}
Ruego diferir sobre la reconexión. El cliente permanece en la lista pero el ID de conexión cambiará. Hago una actualización de la lista estática en las reconexiones para resolver esto.