バックグラウンド サービスでの ASP.NET Core SignalR のホスティング

作成者: Dave PringleBrady Gaster

この記事では、以下に関するガイダンスを提供します。

  • ASP.NET Core にホストされているバックグラウンド ワーカー プロセスを使用した SignalR ハブのホスティング。
  • .NET Core BackgroundService 内から接続されたクライアントへのメッセージの送信。

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

アプリの起動時に SignalR を有効にする

バックグラウンド ワーカー プロセスのコンテキストでの ASP.NET Core SignalR ハブのホスティングは、ASP.NET Core Web アプリ内でのハブのホスティングと同じです。 Program.csbuilder.Services.AddSignalR を呼び出すと、SignalR をサポートするために必要なサービスが ASP.NET Core 依存関係の挿入 (DI) レイヤーに追加されます。 WebApplication app 上で MapHub メソッドが呼び出され、ASP.NET Core 要求パイプラインのハブ エンドポイントを接続します。

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSignalR();
builder.Services.AddHostedService<Worker>();

var app = builder.Build();

app.MapHub<ClockHub>("/hubs/clock");

app.Run();

前の例では、ClockHub クラスにより、厳密に型指定されたハブを作成する Hub<T> クラスが実装されています。 ClockHub は、エンドポイント /hubs/clock で要求に応答するように Program.cs 内で構成されています。

厳密に型指定されたハブの詳細については、ASP.NET Core の SignalR でのハブの使用に関するページをご覧ください。

Note

この機能は Hub<T> クラスに限定されません。 DynamicHub など、ハブから継承するクラスはすべて機能します。

public class ClockHub : Hub<IClock>
{
    public async Task SendTimeToClients(DateTime dateTime)
    {
        await Clients.All.ShowTime(dateTime);
    }
}

厳密に型指定された ClockHub によって使用されるインターフェイスは、IClock インターフェイスです。

public interface IClock
{
    Task ShowTime(DateTime currentTime);
}

バックグラウンド サービスから SignalR ハブを呼び出す

起動時に、Worker クラスである BackgroundService は、AddHostedService を使用して有効化されます。

builder.Services.AddHostedService<Worker>();

また、SignalR は、各ハブが ASP.NET Core の HTTP 要求パイプライン内の個々のエンドポイントに接続されるスタートアップ フェーズ中にも有効になっているので、各ハブはサーバー上の IHubContext<T> によって表されます。 ASP.NET Core の DI 機能を使用すると、ホスティング レイヤーによってインスタンス化された他のクラス (BackgroundService クラス、MVC コントローラー クラス、Razor ページ モデルなど) は、構築中に IHubContext<ClockHub, IClock> のインスタンスを受け入れてサーバー側ハブへの参照を取得できます。

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    private readonly IHubContext<ClockHub, IClock> _clockHub;

    public Worker(ILogger<Worker> logger, IHubContext<ClockHub, IClock> clockHub)
    {
        _logger = logger;
        _clockHub = clockHub;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker running at: {Time}", DateTime.Now);
            await _clockHub.Clients.All.ShowTime(DateTime.Now);
            await Task.Delay(1000, stoppingToken);
        }
    }
}

ExecuteAsync メソッドがバックグラウンド サービス内で繰り返し呼び出されると、サーバーの現在の日付と時刻は ClockHub を使用して接続されたクライアントに送信されます。

バック グラウンド サービスを使用して SignalR イベントに対応する

SignalR 用の JavaScript クライアントを使用するシングル ページ アプリや、ASP.NET Core SignalR .NET Client を使用する .NET デスクトップ アプリのように、BackgroundService または IHostedService の実装を使用して SignalR ハブに接続し、イベントに対応することもできます。

ClockHubClient クラスでは、IClock インターフェイスと IHostedService インターフェイスの両方が実装されます。 この方法では、これをスタートアップ時に有効にして継続的に実行し、サーバーからハブ イベントに対応することができます。

public partial class ClockHubClient : IClock, IHostedService
{
}

初期化中に、ClockHubClient によって HubConnection のインスタンスが作成され、その IClock.ShowTime メソッドがハブの ShowTime イベントのハンドラーとして有効になります。

private readonly ILogger<ClockHubClient> _logger;
private HubConnection _connection;

public ClockHubClient(ILogger<ClockHubClient> logger)
{
    _logger = logger;
    
    _connection = new HubConnectionBuilder()
        .WithUrl(Strings.HubUrl)
        .Build();

    _connection.On<DateTime>(Strings.Events.TimeSent, ShowTime);
}

public Task ShowTime(DateTime currentTime)
{
    _logger.LogInformation("{CurrentTime}", currentTime.ToShortTimeString());

    return Task.CompletedTask;
}

IHostedService.StartAsync 実装では、HubConnection が非同期的に開始されます。

public async Task StartAsync(CancellationToken cancellationToken)
{
    // Loop is here to wait until the server is running
    while (true)
    {
        try
        {
            await _connection.StartAsync(cancellationToken);

            break;
        }
        catch
        {
            await Task.Delay(1000, cancellationToken);
        }
    }
}

IHostedService.StopAsync メソッドの実行中、HubConnection は非同期的に破棄されます。

public async Task StopAsync(CancellationToken cancellationToken)
{
    await _connection.DisposeAsync();
}

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

起動時に SignalR を有効にする

バックグラウンド ワーカー プロセスのコンテキストでの ASP.NET Core SignalR ハブのホスティングは、ASP.NET Core Web アプリ内でのハブのホスティングと同じです。 Startup.ConfigureServices メソッドで services.AddSignalR を呼び出すと、SignalR をサポートするために必要なサービスが ASP.NET Core 依存関係の挿入 (DI) レイヤーに追加されます。 Startup.Configure では、MapHub メソッドが UseEndpoints コールバック内で呼び出され、ASP.NET Core 要求パイプラインのハブ エンドポイントに接続します。

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSignalR();
        services.AddHostedService<Worker>();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapHub<ClockHub>("/hubs/clock");
        });
    }
}

前の例では、ClockHub クラスにより、厳密に型指定されたハブを作成する Hub<T> クラスが実装されています。 ClockHub は、エンドポイント /hubs/clock での要求に応答するように Startup クラス内で構成されています。

厳密に型指定されたハブの詳細については、ASP.NET Core の SignalR でのハブの使用に関するページをご覧ください。

Note

この機能は Hub<T> クラスに限定されません。 DynamicHub など、ハブから継承するクラスはすべて機能します。

public class ClockHub : Hub<IClock>
{
    public async Task SendTimeToClients(DateTime dateTime)
    {
        await Clients.All.ShowTime(dateTime);
    }
}

厳密に型指定された ClockHub によって使用されるインターフェイスは、IClock インターフェイスです。

public interface IClock
{
    Task ShowTime(DateTime currentTime);
}

バックグラウンド サービスから SignalR ハブを呼び出す

起動時に、Worker クラスである BackgroundService は、AddHostedService を使用して有効化されます。

services.AddHostedService<Worker>();

また、SignalR は、各ハブが ASP.NET Core の HTTP 要求パイプライン内の個々のエンドポイントに接続される Startup フェーズ中にも有効になっているので、各ハブはサーバー上の IHubContext<T> によって表されます。 ASP.NET Core の DI 機能を使用すると、ホスティング レイヤーによってインスタンス化された他のクラス (BackgroundService クラス、MVC コントローラー クラス、Razor ページ モデルなど) は、構築中に IHubContext<ClockHub, IClock> のインスタンスを受け入れてサーバー側ハブへの参照を取得できます。

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    private readonly IHubContext<ClockHub, IClock> _clockHub;

    public Worker(ILogger<Worker> logger, IHubContext<ClockHub, IClock> clockHub)
    {
        _logger = logger;
        _clockHub = clockHub;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker running at: {Time}", DateTime.Now);
            await _clockHub.Clients.All.ShowTime(DateTime.Now);
            await Task.Delay(1000);
        }
    }
}

ExecuteAsync メソッドがバックグラウンド サービス内で繰り返し呼び出されると、サーバーの現在の日付と時刻は ClockHub を使用して接続されたクライアントに送信されます。

バック グラウンド サービスを使用して SignalR イベントに対応する

SignalR 用の JavaScript クライアントを使用するシングル ページ アプリや、ASP.NET Core SignalR .NET Client を使用する .NET デスクトップ アプリのように、BackgroundService または IHostedService の実装を使用して SignalR ハブに接続し、イベントに対応することもできます。

ClockHubClient クラスでは、IClock インターフェイスと IHostedService インターフェイスの両方が実装されます。 この方法では、これを Startup 時に有効にして継続的に実行し、サーバーからのハブ イベントに対応することができます。

public partial class ClockHubClient : IClock, IHostedService
{
}

初期化中に、ClockHubClient によって HubConnection のインスタンスが作成され、その IClock.ShowTime メソッドがハブの ShowTime イベントのハンドラーとして有効になります。

private readonly ILogger<ClockHubClient> _logger;
private HubConnection _connection;

public ClockHubClient(ILogger<ClockHubClient> logger)
{
    _logger = logger;
    
    _connection = new HubConnectionBuilder()
        .WithUrl(Strings.HubUrl)
        .Build();

    _connection.On<DateTime>(Strings.Events.TimeSent, ShowTime);
}

public Task ShowTime(DateTime currentTime)
{
    _logger.LogInformation("{CurrentTime}", currentTime.ToShortTimeString());

    return Task.CompletedTask;
}

IHostedService.StartAsync 実装では、HubConnection が非同期的に開始されます。

public async Task StartAsync(CancellationToken cancellationToken)
{
    // Loop is here to wait until the server is running
    while (true)
    {
        try
        {
            await _connection.StartAsync(cancellationToken);

            break;
        }
        catch
        {
            await Task.Delay(1000);
        }
    }
}

IHostedService.StopAsync メソッドの実行中、HubConnection は非同期的に破棄されます。

public Task StopAsync(CancellationToken cancellationToken)
{
    return _connection.DisposeAsync();
}

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

起動時に SignalR を有効にする

バックグラウンド ワーカー プロセスのコンテキストでの ASP.NET Core SignalR ハブのホスティングは、ASP.NET Core Web アプリ内でのハブのホスティングと同じです。 Startup.ConfigureServices メソッドで services.AddSignalR を呼び出すと、SignalR をサポートするために必要なサービスが ASP.NET Core 依存関係の挿入 (DI) レイヤーに追加されます。 Startup.Configure では、UseSignalR メソッドが呼び出され、ASP.NET Core 要求パイプラインのハブ エンドポイントに接続します。

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSignalR();
        services.AddHostedService<Worker>();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseSignalR((routes) =>
        {
            routes.MapHub<ClockHub>("/hubs/clock");
        });
    }
}

前の例では、ClockHub クラスにより、厳密に型指定されたハブを作成する Hub<T> クラスが実装されています。 ClockHub は、エンドポイント /hubs/clock での要求に応答するように Startup クラス内で構成されています。

厳密に型指定されたハブの詳細については、ASP.NET Core の SignalR でのハブの使用に関するページをご覧ください。

Note

この機能は Hub<T> クラスに限定されません。 DynamicHub など、ハブから継承するクラスはすべて機能します。

public class ClockHub : Hub<IClock>
{
    public async Task SendTimeToClients(DateTime dateTime)
    {
        await Clients.All.ShowTime(dateTime);
    }
}

厳密に型指定された ClockHub によって使用されるインターフェイスは、IClock インターフェイスです。

public interface IClock
{
    Task ShowTime(DateTime currentTime);
}

バックグラウンド サービスから SignalR ハブを呼び出す

起動時に、Worker クラスである BackgroundService は、AddHostedService を使用して有効化されます。

services.AddHostedService<Worker>();

また、SignalR は、各ハブが ASP.NET Core の HTTP 要求パイプライン内の個々のエンドポイントに接続される Startup フェーズ中にも有効になっているので、各ハブはサーバー上の IHubContext<T> によって表されます。 ASP.NET Core の DI 機能を使用すると、ホスティング レイヤーによってインスタンス化された他のクラス (BackgroundService クラス、MVC コントローラー クラス、Razor ページ モデルなど) は、構築中に IHubContext<ClockHub, IClock> のインスタンスを受け入れてサーバー側ハブへの参照を取得できます。

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;
    private readonly IHubContext<ClockHub, IClock> _clockHub;

    public Worker(ILogger<Worker> logger, IHubContext<ClockHub, IClock> clockHub)
    {
        _logger = logger;
        _clockHub = clockHub;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker running at: {Time}", DateTime.Now);
            await _clockHub.Clients.All.ShowTime(DateTime.Now);
            await Task.Delay(1000);
        }
    }
}

ExecuteAsync メソッドがバックグラウンド サービス内で繰り返し呼び出されると、サーバーの現在の日付と時刻は ClockHub を使用して接続されたクライアントに送信されます。

バック グラウンド サービスを使用して SignalR イベントに対応する

SignalR 用の JavaScript クライアントを使用するシングル ページ アプリや、ASP.NET Core SignalR .NET Client を使用する .NET デスクトップ アプリのように、BackgroundService または IHostedService の実装を使用して SignalR ハブに接続し、イベントに対応することもできます。

ClockHubClient クラスでは、IClock インターフェイスと IHostedService インターフェイスの両方が実装されます。 この方法では、これを Startup 時に有効にして継続的に実行し、サーバーからのハブ イベントに対応することができます。

public partial class ClockHubClient : IClock, IHostedService
{
}

初期化中に、ClockHubClient によって HubConnection のインスタンスが作成され、その IClock.ShowTime メソッドがハブの ShowTime イベントのハンドラーとして有効になります。

private readonly ILogger<ClockHubClient> _logger;
private HubConnection _connection;

public ClockHubClient(ILogger<ClockHubClient> logger)
{
    _logger = logger;
    
    _connection = new HubConnectionBuilder()
        .WithUrl(Strings.HubUrl)
        .Build();

    _connection.On<DateTime>(Strings.Events.TimeSent, 
        dateTime => _ = ShowTime(dateTime));
}

public Task ShowTime(DateTime currentTime)
{
    _logger.LogInformation("{CurrentTime}", currentTime.ToShortTimeString());

    return Task.CompletedTask;
}

IHostedService.StartAsync 実装では、HubConnection が非同期的に開始されます。

public async Task StartAsync(CancellationToken cancellationToken)
{
    // Loop is here to wait until the server is running
    while (true)
    {
        try
        {
            await _connection.StartAsync(cancellationToken);

            break;
        }
        catch
        {
            await Task.Delay(1000);
        }
    }
}

IHostedService.StopAsync メソッドの実行中、HubConnection は非同期的に破棄されます。

public Task StopAsync(CancellationToken cancellationToken)
{
    return _connection.DisposeAsync();
}

その他のリソース