在后台服务中托管 ASP.NET Core SignalR

作者:Dave PringleBrady Gaster

本文提供以下指导:

  • 使用通过 ASP.NET Core 托管的后台工作进程托管 SignalR 中心。
  • 从 NET Core BackgroundService 中将消息发送到连接的客户端。

查看或下载示例代码如何下载

在应用启动时启用 SignalR

在后台工作进程上下文中托管 ASP.NET Core SignalR 中心与在 ASP.NET Core Web 应用中托管中心完全相同。 在 Program.cs 中,调用 builder.Services.AddSignalR 会将所需的服务添加到 ASP.NET Core 依赖关系注入 (DI) 层来支持 SignalR。 对 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 已在 Program.cs 中配置为响应终结点 /hubs/clock 处的请求。

有关强类型中心的详细信息,请参阅在 SignalR for ASP.NET Core 中使用中心

注意

此功能并不限于中心<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 中心

在启动过程中,将使用 AddHostedService 启用 WorkerBackgroundService

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客户端的 .NET 桌面应用一样,BackgroundServiceIHostedService 实现也可用于连接到 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();
}

查看或下载示例代码如何下载

在 startup 中启用 SignalR

在后台工作进程上下文中托管 ASP.NET Core SignalR 中心与在 ASP.NET Core Web 应用中托管中心完全相同。 在 Startup.ConfigureServices 方法中,调用 services.AddSignalR 会将所需的服务添加到 ASP.NET Core Dependency Injection (DI) 层以支持 SignalR。 在 Startup.Configure 中,会在 UseEndpoints 回叫中调用 MapHub 方法来连接 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 已在 Startup 中配置为响应终结点 /hubs/clock 处的请求。

有关强类型中心的详细信息,请参阅在 SignalR for ASP.NET Core 中使用中心

注意

此功能并不限于中心<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 中心

在启动过程中,将使用 AddHostedService 启用 WorkerBackgroundService

services.AddHostedService<Worker>();

由于 SignalR 也会在 Startup 期间启用,其中每个中心会附加到 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);
        }
    }
}

由于会在后台服务中以迭代方式调用 ExecuteAsync 方法,因此,会使用 ClockHub 将服务器的当前日期和时间发送到连接的客户端。

使用后台服务对 SignalR 事件进行回应

与使用适用于 SignalR 的 JavaScript 客户端的单页应用或使用 ASP.NET Core SignalR .NET客户端的 .NET 桌面应用一样,BackgroundServiceIHostedService 实现也可用于连接到 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();
}

查看或下载示例代码如何下载

在 startup 中启用 SignalR

在后台工作进程上下文中托管 ASP.NET Core SignalR 中心与在 ASP.NET Core Web 应用中托管中心完全相同。 在 Startup.ConfigureServices 方法中,调用 services.AddSignalR 会将所需的服务添加到 ASP.NET Core Dependency Injection (DI) 层以支持 SignalR。 在 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 已在 Startup 中配置为响应终结点 /hubs/clock 处的请求。

有关强类型中心的详细信息,请参阅在 SignalR for ASP.NET Core 中使用中心

注意

此功能并不限于中心<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 中心

在启动过程中,将使用 AddHostedService 启用 WorkerBackgroundService

services.AddHostedService<Worker>();

由于 SignalR 也会在 Startup 期间启用,其中每个中心会附加到 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);
        }
    }
}

由于会在后台服务中以迭代方式调用 ExecuteAsync 方法,因此,会使用 ClockHub 将服务器的当前日期和时间发送到连接的客户端。

使用后台服务对 SignalR 事件进行回应

与使用适用于 SignalR 的 JavaScript 客户端的单页应用或使用 ASP.NET Core SignalR .NET客户端的 .NET 桌面应用一样,BackgroundServiceIHostedService 实现也可用于连接到 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();
}

其他资源