ハブの外部からメッセージを送信する

SignalR ハブは、SignalR サーバーに接続されているクライアントにメッセージを送信するための中核となる抽象化です。 また、IHubContext サービスを使ってアプリ内の他の場所からメッセージを送信することもできます。 この記事では、SignalRIHubContext にアクセスしてハブの外部からクライアントに通知を送信する方法について説明します。

Note

IHubContext はクライアントに通知を送信するためのものであり、Hub に対してメソッドを呼び出すためには使われません。

サンプル コードを表示またはダウンロードする (ダウンロード方法)

IHubContext のインスタンスを取得する

ASP.NET Core SignalR では、依存関係の挿入を介して IHubContext のインスタンスにアクセスできます。 コントローラーやミドルウェアなどの DI サービスに IHubContext のインスタンスを挿入することができます。 そのインスタンスを使ってクライアントにメッセージを送信します。

コントローラーに IHubContext のインスタンスを挿入する

コントローラーに IHubContext のインスタンスを挿入するには、コンストラクターに追加します。

public class HomeController : Controller
{
    private readonly IHubContext<NotificationHub> _hubContext;

    public HomeController(IHubContext<NotificationHub> hubContext)
    {
        _hubContext = hubContext;
    }
}

IHubContext のインスタンスにアクセスして、そのハブ内からのようにクライアント メソッドを呼び出します。

public async Task<IActionResult> Index()
{
    await _hubContext.Clients.All.SendAsync("Notify", $"Home page loaded at: {DateTime.Now}");
    return View();
}

ミドルウェアで IHubContext のインスタンスを取得する

次のように、ミドルウェア パイプライン内で IHubContext にアクセスします。

app.Use(async (context, next) =>
{
    var hubContext = context.RequestServices
                            .GetRequiredService<IHubContext<ChatHub>>();
    //...
    
    if (next != null)
    {
        await next.Invoke();
    }
});

Note

Hub クラスの外部からクライアント メソッドを呼び出す場合、その呼び出しに関連付けられた呼び出し元はありません。 そのため、ConnectionIdCallerOthers のプロパティにはアクセスできません。

ユーザーを接続 ID にマップし、そのマッピングを保持する必要があるアプリは、次のいずれかを実行できます。

  • 単一または複数の接続のマッピングをグループとして保持します。 詳細については、「 SignalRのグループ」を参照してください。
  • シングルトン サービスを介して接続とユーザー情報を保持します。 詳細については、「ハブにサービスを挿入する」を参照してください。 シングルトン サービスでは、次のような任意のストレージ方法を使用できます。
    • ディクショナリ内のメモリ内ストレージ。
    • 永続的な外部ストレージ。 たとえば、データベースや Azure.Data.Tables NuGet パッケージを使用する Azure Table Storage などです。
  • クライアント間で接続 ID を渡します。

IHost から IHubContext のインスタンスを取得する

Web ホストから IHubContext にアクセスする方法は、サードパーティの依存関係の挿入フレームワークを使うなど、ASP.NET Core の外部にある領域と統合する場合に便利です。

    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();
            var hubContext = host.Services.GetService(typeof(IHubContext<ChatHub>));
            host.Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder => {
                    webBuilder.UseStartup<Startup>();
                });
    }

厳密に型指定された HubContext を挿入する

厳密に型指定された HubContext を挿入するには、ハブが Hub<T> から継承されていることを確認します。 IHubContext<THub> ではなく IHubContext<THub, T> インターフェイスを使って挿入します。

public class ChatController : Controller
{
    public IHubContext<ChatHub, IChatClient> _strongChatHubContext { get; }

    public ChatController(IHubContext<ChatHub, IChatClient> chatHubContext)
    {
        _strongChatHubContext = chatHubContext;
    }

    public async Task SendMessage(string user, string message)
    {
        await _strongChatHubContext.Clients.All.ReceiveMessage(user, message);
    }
}

詳細については、「厳密に型指定されたハブ」を参照してください。

ジェネリック コードで IHubContext を使う

挿入された IHubContext<THub> インスタンスは、ジェネリック Hub 型が指定されていなくても IHubContext にキャストできます。

class MyHub : Hub
{ }

class MyOtherHub : Hub
{ }

app.Use(async (context, next) =>
{
    var myHubContext = context.RequestServices
                            .GetRequiredService<IHubContext<MyHub>>();
    var myOtherHubContext = context.RequestServices
                            .GetRequiredService<IHubContext<MyOtherHub>>();
    await CommonHubContextMethod((IHubContext)myHubContext);
    await CommonHubContextMethod((IHubContext)myOtherHubContext);

    await next.Invoke();
}

async Task CommonHubContextMethod(IHubContext context)
{
    await context.Clients.All.SendAsync("clientMethod", new Args());
}

これは、次の場合に便利です。

  • アプリに使われている特定の Hub 型への参照を持たないライブラリを書く。
  • 複数の異なる Hub の実装に適用できる汎用的なコードを書く。

その他のリソース