0Pricing
C# Academy · レッスン

グループ、ユーザーと接続管理

グループを管理し、特定のユーザーや接続を対象にして、接続・切断のライフサイクルイベントを処理します。

「グループ、ユーザーと接続管理」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全4レッスンが含まれています。

グループとユーザーを使用する理由

実際のアプリケーションで、すべてのクライアントにブロードキャストしたい場面はほとんどありません。SignalR のグループを使用すると接続の一部(チャットルームなど)を対象にでき、ユーザーを使用すると特定の認証済みユーザーに属するすべての接続にアドレス指定できます。

接続をグループに追加する

接続 ID とグループ名を指定して Groups.AddToGroupAsync() を呼び出します。1 つの接続を同時に複数のグループに所属させることができます。

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

グループに送信する

Clients.Group(name) を使用して、グループ内のすべての接続に送信します。その他の対象指定オプションには、GroupExcept と Groups(一度に複数のグループ)が含まれます。

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

ユーザーベースの対象指定

ユーザーが複数のブラウザタブやデバイスを開いている場合、そのユーザーには複数の接続があります。Clients.User(userId) を使用すると、そのユーザーのすべての接続に同時に送信できます。

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

カスタムユーザー ID プロバイダー

SignalR は既定で NameIdentifier クレームをユーザー ID として使用します。別のクレームやカスタムロジックを使用するには、IUserIdProvider を実装します。

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

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

接続状態を追跡する

SignalR は切断後も接続状態を保持しません。ユーザー ID と接続 ID のマッピングを、スレッドセーフなディクショナリ(単一サーバー)または Redis(複数サーバー)で独自に追跡してください。

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

OnConnected/OnDisconnected でグループを自動管理する

一般的なパターンとして、OnConnectedAsync で認証済みユーザーを個人用グループに自動的に追加します。これにより、カスタムの追跡処理を行わずにグループ名でユーザーを対象にできます。

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

グループを使用してバックグラウンドサービスから送信する

IHubContext を使用すると、バックグラウンドサービスからグループやユーザーを対象にできます。システム内のイベントをきっかけにした非同期通知に最適です。

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

グループメンバーシップの永続化

グループメンバーシップは永続化されません。サーバーが再起動したり、Redis バックプレーンの状態が失われたりすると、グループは空になります。クライアントの 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);
    }
});

プレゼンス:オンライン/オフラインの検出

OnConnected/OnDisconnected でユーザーごとの接続数を数え、関連するグループにプレゼンスイベントをブロードキャストすることで、オンライン状態を追跡します。

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

確認問題

ユーザーがブラウザーのタブを3つ開いている場合、Clients.User(userId) は何を行いますか?

まとめ:グループ、ユーザー、接続管理

主なポイント:

  • Groups.AddToGroupAsync/RemoveFromGroupAsync:グループメンバーシップを管理します
  • Clients.Group(name):接続の一部にメッセージを送信します
  • Clients.User(userId):ユーザーのすべての接続にメッセージを送信します
  • カスタムユーザー ID クレームには IUserIdProvider を実装します
  • グループは永続化されないため、再接続時に再参加させます
  • プレゼンス検出のため、ConcurrentDictionary で接続数を追跡します

よくある質問

「グループ、ユーザーと接続管理」レッスンは無料ですか?

はい。「グループ、ユーザーと接続管理」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全4レッスンが含まれています。

「グループ、ユーザーと接続管理」で何を学びますか?

グループを管理し、特定のユーザーや接続を対象にして、接続・切断のライフサイクルイベントを処理します。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

C# Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「グループ、ユーザーと接続管理」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このC# Academyレッスンでコードを書いて実行できますか?

はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. SignalR Hubと接続
  2. グループ、ユーザーと接続管理
  3. 型付きHub
  4. Redis Backplaneによるスケーリング
← C# Academyに戻る