ASP.NET Core SignalR の動作のしくみ

完了

サーバーと Hub クラス

Hub クラスは SignalR サーバーの概念です。 これは、Microsoft.AspNetCore.SignalR 名前空間内で定義され、Microsoft.AspNetCore.SignalR NuGet パッケージに含まれます。 Microsoft.NET.Sdk.Web SDK を対象とする ASP.NET Core Web アプリは、共有フレームワークの一部として既に使用可能になっているため、SignalR のパッケージ参照を追加する必要はありません。

Hub は "ルート" を介して公開されます。 たとえば、OrdersHub の実装を表すために https://www.contoso-pizza.com/hubs/orders ルートを使用できます。 作成者はさまざまなハブ API を使用して、メソッドとイベントを定義できます。

ハブでメソッドを公開するための 2 つのモダリティがあります。 次の型のサブクラスを作成し、メソッドを記述します。

  • Hub: 標準ハブ。
  • Hub<T>: 厳密に型指定されたジェネリック ハブ。

Hub

参照のポイントとして、次の Notification オブジェクトを考えてみましょう。

namespace RealTime.Models;

public record Notification(string Text, DateTime Date);

このオブジェクトは、.NET クライアント SDK を使用するときに共有できるので、サーバーとクライアントにまったく同じオブジェクトが存在します。 次のような通知ハブがあるとします。

using Microsoft.AspNetCore.SignalR;
using System;
using System.Threading.Tasks;
using RealTime.Models;

namespace ExampleServer.Hubs;

public sealed class NotificationHub : Hub
{
    public Task NotifyAll(Notification notification) =>
        Clients.All.SendAsync("NotificationReceived", notification);
}

メソッドとイベントの違いについては、前のハブ実装ではメソッドは NotifyAll で、イベントは NotificationReceived です。 NotificationHubHub のサブクラスです。 NotifyAll メソッドは Task を返し、単一の Notification パラメーターを受け取ります。 このメソッドは、すべての接続済みクライアントを表す Clients.All から SendAsync への呼び出しとして表されます。 notification インスタンスに応じて、NotificationReceived イベントが発生します。

IHubContext インスタンス

イベントは、Hub または IHubContext インスタンスから発生します。 SignalR ハブは、SignalR サーバーに接続されているクライアントにメッセージを送信するためのコア抽象化です。 これは、次のいずれかの種類を使用することによって、アプリ内の他の場所からメッセージを送信することもできます。

  • IHubContext<THub>: THub が標準ハブを表すコンテキスト。
  • IHubContext<THub,T>:THub が厳密に型指定されたジェネリック ハブを表し、T が対応する型のクライアントを表すコンテキスト。

重要

IHubContext は、クライアントに通知を送信する場合に使用します。 Hub でメソッドを呼び出すためには使用され "ません"。

IHubContext の例

前の通知ハブの実装例では、IHubContext<NotificationHub> を次のように使用できます。

using Microsoft.AspNetCore.SignalR;
using System;
using System.Threading.Tasks;
using RealTime.Models;

namespace ExampleServer.Services;

public sealed class NotificationService(
    IHubContext<NotificationHub> hubContext)
{
    public Task SendNotificationAsync(Notification notification) =>
        notification is not null
            ? hubContext.Clients.All.SendAsync("NotificationReceived", notification)
            : Task.CompletedTask;
}

上の C# コードは、IHubContext<NotificationHub> に依存してクライアントのコンテキスト リストにアクセスし、通知をブロードキャストする機能を公開します。 スコープ内の取り込まれた hubContext プライマリ コンストラクター パラメーターは、"NotificationReceived" イベントを発生させるために使われますが、ハブの NotifyAll メソッドを呼び出すために使われるものではありません。

メソッド

Hub または Hub<T> メソッドは、他の C# メソッドと同様です。 戻り値の型、メソッド名、およびパラメーターを定義します。

  • ハブ メソッドの最も一般的な戻り値の型は、Task または Task<TResult> で、これは非同期のハブ操作を表します。
  • メソッド名は、クライアントからメソッドを呼び出すために使用します。 HubMethodNameAttribute を使用してカスタマイズできます。
  • パラメーターは省略可能ですが、定義されている場合、クライアントで対応する引数を指定する必要があります。

メソッドは、イベントの発生に必要ありませんが、しばしばイベントを発生します。

イベント

イベントは、クライアントから名前でサブスクライブできます。 サーバーは、イベントを発生する役割を担います。 HubHub<T>IHubContext<THub>IHubContext<THub, T> イベントは、"名前付き" で、最大 10 個のパラメーターを定義できます。 イベントはサーバー上で発生し、対象となるクライアントによって処理されます。 クライアントは、そのハブの接続でイベントにサブスクライブしている場合、対象と見なされます。 クライアントは、呼び出しの結果としてイベントを発生するハブ メソッドを呼び出す際に、間接的にイベントをトリガーすることができます。 しかし、これはサーバーの役割なので、クライアントがイベントを直接トリガーすることはできません。

イベント クライアント スコープ

イベントを IClientProxy インスタンスから呼び出せます。 Clients 型から IHubClients および IHubCallerClients インターフェイスを実装します。 特定の IClientProxy インスタンスにスコープを設定する方法はたくさんあります。 Hub.Clients プロパティから次のスコープを対象にできます。

メンバー 詳細
All すべての接続済みクライアント (ブロードキャストなど)。
AllExcept 指定した接続を除くすべての接続済みクライアント (フィルター処理されたブロードキャストなど)。
Caller メソッドをトリガーした接続済みクライアント (エコーなど)。
Client 指定されたクライアント接続 (単一接続)。
Clients 指定されたクライアント接続 (複数接続)。
Group 指定したグループ内のすべての接続済みクライアント。
GroupExcept 指定したグループ内のすべての接続済みクライアント (指定した接続を除く)。
Groups 指定したグループ内のすべての接続済みクライアント (複数グループ)。
Others メソッドをトリガーしたクライアントを除くすべての接続済みクライアント。
OthersInGroup メソッドをトリガーしたクライアントを除く、指定したグループ内のすべての接続済みクライアント。
User 指定した 1 人のユーザーに対するすべての接続済みクライアント (1 人のユーザーが複数のデバイスに接続できます)。
Users 指定した複数のユーザーに対するすべての接続済みクライアント。

スコープの例

ハブがターゲット クライアントにメッセージを送信する方法をイメージするのに役立ついくつかの画像に注目してみましょう。 見やすくするために画像を拡大できます。

  • 全員にブロードキャストする

    Clients.All 構文を使用してメッセージを送信する ASP.NET Core SignalR ハブ。

    すべての接続済みクライアントは、そのグループに属しているかどうかに関わらずこのメッセージを受信します。

  • 分離ユーザー

    Clients.User 構文を使用してメッセージを送信する ASP.NET Core SignalR ハブ。

    現在使用しているデバイスの数に関わらず、1 人のユーザーがこのメッセージを受信します。

  • 分離グループ

    Clients.Group 構文を使用してメッセージを送信する ASP.NET Core SignalR ハブ。

    このメッセージを受信できるのは、特定のグループに属しているクライアントのみです。

クライアントと HubConnection クラス

HubConnection クラスは SignalR クライアントの概念であり、サーバー Hubへの接続を表します。 これは、Microsoft.AspNetCore.SignalR.Client 名前空間内で定義され、Microsoft.AspNetCore.SignalR.Client NuGet パッケージに含まれます。

ビルダー パターン、およびそれに対応する HubConnectionBuilder 型を使用して HubConnection を作成します。 ハブのルート (または System.Uri) を指定すると、HubConnection を作成できます。 ビルダーでは、特に、ログ、目的のプロトコル、認証トークンの転送、自動再接続など、追加の構成オプションも指定できます。

HubConnection API は、開始関数と停止関数を公開します。これらは、サーバーへの接続の開始と停止に使用されます。 また、ストリーミング、ハブ メソッドの呼び出し、イベントへのサブスクライブのための機能もあります。

HubConnection 作成の例

.NET SignalR クライアント SDK から HubConnection オブジェクトを作成するには、HubConnectionBuilder 型を使用します。

using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Threading.Tasks;
using RealTime.Models;

namespace ExampleClient;

public sealed class Consumer : IAsyncDisposable
{
    private readonly string HostDomain =
        Environment.GetEnvironmentVariable("HOST_DOMAIN");
    
    private HubConnection _hubConnection;

    public Consumer()
    {
        _hubConnection = new HubConnectionBuilder()
            .WithUrl(new Uri($"{HostDomain}/hub/notifications"))
            .WithAutomaticReconnect()
            .Build();
    }

    public Task StartNotificationConnectionAsync() =>
        _hubConnection.StartAsync();

    public async ValueTask DisposeAsync()
    {
        if (_hubConnection is not null)
        {
            await _hubConnection.DisposeAsync();
            _hubConnection = null;
        }
    }
}

ハブ メソッドを呼び出す

クライアントで正常に開始されたクライアント HubConnection インスタンスが得られた場合、そのクライアントは InvokeAsync または SendAsync 拡張機能を使用してハブ上でメソッドを呼び出すことができます。 ハブ メソッドが Task<TResult> を返す場合、InvokeAsync<TResult> の結果は TResult 型になります。 ハブ メソッドが Task を返す場合は、結果はありません。 InvokeAsyncSendAsync はどちらも、ハブ メソッドの名前と、0 から 10 個のパラメーターが必要です。

  • InvokeAsync: 指定したメソッド名とオプションの引数を使用して、サーバー上のハブ メソッドを呼び出します。
  • SendAsync: 指定したメソッド名とオプションの引数を使用して、サーバー上のハブ メソッドを呼び出します。 このメソッドは、受信側からの応答を "待機しません"。

ハブ メソッド呼び出しの例

SendNotificationAsync で前の Consumer クラスにメソッドを追加すると、SendNotificationAsync_hubConnection にデリゲートされ、Notification インスタンスに応じて、サーバーのハブ上で NotifyAll メソッドが呼び出されます。

public Task SendNotificationAsync(string text) =>
    _hubConnection.InvokeAsync(
        "NotifyAll", new Notification(text, DateTime.UtcNow));

イベントを処理する

イベントを処理するには、ハンドラーに HubConnection インスタンスを登録します。 ハブ メソッドの名前と 0 から 8 個のパラメーターを指定して、HubConnectionExtensions.On オーバーロードのいずれかを呼び出します。 ハンドラーは、次の Action バリエーションのいずれかを満たします。

または、Func<TResult> である非同期ハンドラー API を使用できます。ここで、TResultTask バリエーションです。

イベント ハンドラーを登録した結果は IDisposable で、これがサブスクリプションとして機能します。 ハンドラーの登録を解除するには、Dispose を呼び出します。

イベント登録の例

前の Consumer クラスを更新し、ハンドラーを指定して On を呼び出すことにより、イベントに登録します。

using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Threading.Tasks;
using RealTime.Models;

namespace ExampleClient;

public sealed class Consumer : IAsyncDisposable
{
    private readonly string HostDomain =
        Environment.GetEnvironmentVariable("HOST_DOMAIN");
    
    private HubConnection _hubConnection;

    public Consumer()
    {
        _hubConnection = new HubConnectionBuilder()
            .WithUrl(new Uri($"{HostDomain}/hub/notifications"))
            .WithAutomaticReconnect()
            .Build();

        _hubConnection.On<Notification>(
            "NotificationReceived", OnNotificationReceivedAsync);
    }

    private async Task OnNotificationReceivedAsync(Notification notification)
    {
        // Do something meaningful with the notification.
        await Task.CompletedTask;
    }

    // Omitted for brevity.
}

OnNotificationReceivedAsync メソッドは、サーバーのハブ インスタンスが "NotificationReceived" イベントを発生すると呼び出されます。

Contoso Pizza ライブ注文の更新

Web アプリケーション用のサーバー コードでは、Hub を実装し、ルートをクライアントに公開する必要があります。 Hub では、注文オブジェクトの一意識別子を使用して、追跡用のグループを作成できます。 そのようにすると、注文状態の変更の更新すべてをこのグループ内で通信できます。

Contoso Pizza アプリケーションが Blazor WebAssembly アプリであることを示すため、クライアント コードも更新する必要があります。 JavaScript SDK か .NET クライアント SDK を使用できます。 次に、クライアント側のポーリング機能を、HubConnection をビルドするコードに置き換え、サーバーへの接続を開始します。 ユーザーが注文追跡ページに移動する際に、コードは変更の更新が送信される注文の特定のグループに参加する必要があります。 注文状態の変更に関するイベントをサブスクライブし、それに応じて処理します。