0Pricing
C# Academy · Lezione

Gruppi, utenti e gestione delle connessioni

Gestisca i gruppi, si rivolga a utenti o connessioni specifici e gestisca gli eventi del ciclo di vita di connessione e disconnessione.

Gruppi, utenti e gestione delle connessioni è una lezione C# Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C# Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C# Academy include 4 lezioni in totale.

Perché utilizzare gruppi e utenti?

Nelle applicazioni reali raramente si desidera trasmettere a tutti i client. I gruppi di SignalR consentono di indirizzare sottoinsiemi di connessioni (ad esempio una chat room), mentre gli utenti consentono di inviare messaggi a tutte le connessioni appartenenti a uno specifico utente autenticato.

Aggiungere connessioni ai gruppi

Chiami Groups.AddToGroupAsync() con l'ID della connessione e il nome di un gruppo. Una connessione può appartenere contemporaneamente a più gruppi.

public class ChatHub : Hub
{
    public async Task JoinRoom(string roomName)
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
        await Clients.Group(roomName)
                     .SendAsync("UserJoined", Context.User!.Identity!.Name);
    }

    public async Task LeaveRoom(string roomName)
    {
        await Groups.RemoveFromGroupAsync(Context.ConnectionId, roomName);
        await Clients.Group(roomName)
                     .SendAsync("UserLeft", Context.User!.Identity!.Name);
    }
}

Inviare messaggi ai gruppi

Utilizzi Clients.Group(name) per inviare messaggi a tutte le connessioni di un gruppo. Tra le altre opzioni di indirizzamento vi sono GroupExcept e Groups (più gruppi contemporaneamente).

public async Task SendToRoom(string roomName, string message)
{
    var sender = Context.User!.Identity!.Name;

    // Everyone in the room
    await Clients.Group(roomName)
                 .SendAsync("ReceiveMessage", sender, message);

    // Everyone in the room except the sender
    await Clients.GroupExcept(roomName, Context.ConnectionId)
                 .SendAsync("ReceiveMessage", sender, message);
}

Indirizzamento basato sull'utente

Quando un utente ha aperte più schede del browser o più dispositivi, dispone di più connessioni. Clients.User(userId) invia il messaggio contemporaneamente a tutte le connessioni di quell'utente.

// Send to all connections of user with ID "user-123"
await Clients.User("user-123")
             .SendAsync("Notification", "Your order has shipped!");

// SignalR uses the NameIdentifier claim by default.
// Context.UserIdentifier returns the current user's ID.
var myId = Context.UserIdentifier;
await Clients.User(myId!).SendAsync("Ack", "Server received your message");

Provider personalizzato dell'ID utente

Per impostazione predefinita, SignalR utilizza il claim NameIdentifier come ID utente. Implementi IUserIdProvider per utilizzare un claim diverso o una logica personalizzata.

public class EmailUserIdProvider : IUserIdProvider
{
    public string? GetUserId(HubConnectionContext connection)
    {
        return connection.User?.FindFirst(ClaimTypes.Email)?.Value;
    }
}

// Register:
builder.Services.AddSingleton<IUserIdProvider, EmailUserIdProvider>();

Monitorare lo stato delle connessioni

SignalR non mantiene lo stato della connessione tra una disconnessione e l'altra. Gestisca una propria mappatura degli ID utente agli ID delle connessioni in un dizionario thread-safe (su un singolo server) o in Redis (su più server).

public class ConnectionTracker
{
    private readonly ConcurrentDictionary<string, HashSet<string>> _userConnections
        = new();

    public void AddConnection(string userId, string connectionId)
        => _userConnections.GetOrAdd(userId, _ => new()).Add(connectionId);

    public void RemoveConnection(string userId, string connectionId)
    {
        if (_userConnections.TryGetValue(userId, out var conns))
        {
            conns.Remove(connectionId);
            if (conns.Count == 0) _userConnections.TryRemove(userId, out _);
        }
    }

    public IEnumerable<string> GetConnections(string userId)
        => _userConnections.TryGetValue(userId, out var c) ? c : Enumerable.Empty<string>();
}

Gestire automaticamente i gruppi in OnConnected/OnDisconnected

Un modello comune consiste nell'aggiungere automaticamente gli utenti autenticati al proprio gruppo personale in OnConnectedAsync, così da poterli indirizzare tramite il nome del gruppo senza un monitoraggio personalizzato.

public override async Task OnConnectedAsync()
{
    // Add to a personal group named by user ID
    var userId = Context.UserIdentifier;
    if (userId is not null)
        await Groups.AddToGroupAsync(Context.ConnectionId, userId);
    await base.OnConnectedAsync();
}

// Now send to a user by their personal group:
await _hub.Clients.Group(userId)
           .SendAsync("Update", payload);

Inviare messaggi dai servizi in background con i gruppi

Utilizzando IHubContext può indirizzare gruppi e utenti dai servizi in background: è perfetto per le notifiche asincrone attivate dagli eventi nel sistema.

public class OrderShippedHandler : INotificationHandler<OrderShipped>
{
    private readonly IHubContext<OrderHub> _hub;
    public OrderShippedHandler(IHubContext<OrderHub> hub) => _hub = hub;

    public async Task Handle(OrderShipped notification, CancellationToken ct)
    {
        // Push to the customer's personal group
        await _hub.Clients.Group(notification.CustomerId.ToString())
                  .SendAsync("OrderShipped",
                      new { notification.OrderId, notification.TrackingNumber },
                      ct);
    }
}

Persistenza dell'appartenenza ai gruppi

L'appartenenza ai gruppi non viene mantenuta: se il server viene riavviato o un backplane Redis perde lo stato, i gruppi risultano vuoti. Alla riconnessione, si unisca nuovamente ai gruppi utilizzando l'evento client onreconnected.

// JavaScript client: re-join groups after reconnect
connection.onreconnected(async connectionId => {
    console.log(`Reconnected as ${connectionId}`);
    // Re-join previously joined rooms
    for (const room of joinedRooms) {
        await connection.invoke("JoinRoom", room);
    }
});

Presenza: rilevamento online/offline

Tracci lo stato online contando le connessioni per utente in OnConnected/OnDisconnected e trasmettendo gli eventi di presenza ai gruppi interessati.

public override async Task OnConnectedAsync()
{
    var userId = Context.UserIdentifier!;
    var count = _tracker.AddConnection(userId, Context.ConnectionId);
    if (count == 1) // first connection for this user
        await Clients.Others.SendAsync("UserOnline", userId);
    await base.OnConnectedAsync();
}

public override async Task OnDisconnectedAsync(Exception? ex)
{
    var userId = Context.UserIdentifier!;
    var count = _tracker.RemoveConnection(userId, Context.ConnectionId);
    if (count == 0) // no more connections
        await Clients.Others.SendAsync("UserOffline", userId);
    await base.OnDisconnectedAsync(ex);
}

Verifica rapida

Quando un utente ha aperte tre schede del browser, che cosa fa Clients.User(userId)?

Riepilogo: gruppi, utenti e gestione delle connessioni

Concetti chiave:

  • Groups.AddToGroupAsync/RemoveFromGroupAsync: gestiscono l'appartenenza ai gruppi
  • Clients.Group(name): invia a un sottoinsieme di connessioni
  • Clients.User(userId): invia a tutte le connessioni di un utente
  • Implementi IUserIdProvider per usare attestazioni personalizzate per l'ID utente
  • I gruppi non vengono mantenuti: si unisca nuovamente ai gruppi alla riconnessione
  • Tracci il numero di connessioni in ConcurrentDictionary per rilevare la presenza

Domande Frequenti

La lezione «Gruppi, utenti e gestione delle connessioni» è gratuita?

Sì — il testo completo di «Gruppi, utenti e gestione delle connessioni» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C# Academy, passa a CoddyKit PRO. Il corso C# Academy include 4 lezioni in totale.

Cosa imparerò in «Gruppi, utenti e gestione delle connessioni»?

Gestisca i gruppi, si rivolga a utenti o connessioni specifici e gestisca gli eventi del ciclo di vita di connessione e disconnessione. Eserciti C# Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare C# Academy?

Non è richiesta alcuna esperienza precedente. C# Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Gruppi, utenti e gestione delle connessioni»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione C# Academy?

Sì. Ogni lezione C# Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Hub e connessioni SignalR
  2. Gruppi, utenti e gestione delle connessioni
  3. Hub fortemente tipizzati
  4. Scalabilità con Redis Backplane
← Torna a C# Academy