如何将SignalR添加到.NET Core Windows服务中?

4

我可以找到添加它到MVC的教程,甚至从2014年开始,有一篇文章解释如何将其添加到.NET 4.7 Windows服务中。

然而,在.NET Core 3.1 Windows服务中,我无法弄清楚如何做到这一点。

大多数教程似乎围绕着一个Startup.cs文件展开,但在Windows服务中不存在该文件。是我能够从Microsoft找到的最新教程,但它使用的是Web应用程序而不是Windows服务。

Windows服务使用以下代码运行:

var builder = new HostBuilder() .ConfigureServices((hostContext, services) => { services.AddHostedService<MyWindowsService>(); });

我认为SignalR需要在这里设置。

我发现有些证据表明你可以使用 WebApp.Start("http://localhost:8080");,但这是 OWIN。我找到的示例(比上面的旧版本)有一个 Startup 类,但没有指示如何调用此类。它需要一个 IAppBuilder,并且有一个在其上添加 SignalR 的方法。但是,IAppBuilder 似乎不是 .NET Core,也找不到任何类型的 SignalR 方法。
我想知道是否有人能够指点我正确的方向?
2个回答

6

如果想要开发一个在Windows服务中运行的自托管ASPNETCore SignalR中心,这是我的精简代码。 (免责声明:我是ASPNET Core的新手,不知道更有经验的人是否会赞同这种方法。) 神奇的是".UseStartup();"调用。

  1. Create a new service project using the VS 2019 "Worker Service" C# template.

  2. Edit the service's .csproj file and insert the lines:

        <ItemGroup>
            <FrameworkReference Include="Microsoft.aspNetCore.App" />
        </ItemGroup>
    
  3. Create a Startup class:

    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.DependencyInjection;
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace My.SignalRCore.Service
    {
        public class Startup
        {
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddSignalR();
                services.AddHostedService<Worker>();
            }
    
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                app.UseRouting();               // pre-requisite for app.UseEndpoints()
                app.UseEndpoints(endpoints =>
                {
                    string url = $"/ServerHub";
                    endpoints.MapHub<MyHub>(url);
                });
            }
        }
    }
    
  4. Create a MyHub : Hub class:

    using Microsoft.AspNetCore.SignalR;
    using Microsoft.Extensions.Logging;
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace My.SignalRCore.Service
    {
        public class MyHub : Hub
        {
            public ILogger<Worker> _logger = null;
    
            public MyHub(ILogger<Worker> logger)
            {
                _logger = logger;
                //_logger.LogInformation($"{DateTimeOffset.Now} MyHub.Constructor()");
    
            }
            public async Task ProcessClientMessage(string user, string message)
            {
                // process an incoming message from a connected client
                _logger.LogInformation($"{DateTime.Now.ToString("hh:mm:ss.fff")}  MyHub.ProcessClientMessage({user}, {message})");
    
            }
        }
    }
    
  5. Amend the Program class to use a "UseStartup" call:

    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Hosting;
    using System;
    
    namespace My.SignalRCore.Service
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                CreateHostBuilder(args).Build().Run();
            }
    
            public static IHostBuilder CreateHostBuilder(string[] args) =>
                Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                    webBuilder.UseUrls("http://*:12457");
                });
        }
    }
    
  6. Add a hub reference (if needed) in the Worker class:

    using Microsoft.AspNetCore.SignalR;
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Logging;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace My.SignalRCore.Service
    {
        public class Worker : BackgroundService
        {
            private readonly ILogger<Worker> _logger;
            private readonly IHubContext<MyHub> _signalRHub;
    
            public Worker(ILogger<Worker> logger, IHubContext<MyHub> signalRHub)
            {
                _logger = logger;
                _signalRHub = signalRHub;
            }
    
            protected override async Task ExecuteAsync(CancellationToken stoppingToken)
            {
                while (!stoppingToken.IsCancellationRequested)
                {
                    await Task.Delay(15000, stoppingToken);
                    _logger.LogInformation($"{DateTime.Now.ToString("hh:mm:ss.fff")} Sending ping to all clients");
                    await _signalRHub.Clients.All.SendAsync("ReceiveMessage", "Server", "ping");
                }
            }
        }
    }
    
这就是服务器代码的全部内容。虽然我还没有将其安装为服务,但它可以作为控制台应用程序运行。 在非开发机器上,您可能需要安装APSNET CORE 3.1运行时,可以在此处下载: https://dotnet.microsoft.com/download/dotnet/3.1 客户端方面:
  1. Install nuget package: Microsoft.AspNetCore.SignalR.Client

  2. Create a client class along the lines of (note: the reconnect code here isn't working):

    using Microsoft.AspNetCore.SignalR.Client;
    using System;
    using System.Threading.Tasks;
    
    namespace My.SignalRCoreClientLib
    {
        public class SignalRCoreClientLib
        {
            public EventHandler<string> MessageEvent;
    
            private HubConnection _connection;
    
            public async Task Connect(string serverIp, int port)
            {
                if (_connection == null)
                {
                    _connection = new HubConnectionBuilder()
                        .WithUrl($"http://{serverIp}:{port}/ServerHub")
                        .Build();
    
                    _connection.Closed += async (error) =>
                    {
                        await Task.Delay(new Random().Next(0, 5) * 1000);
                        await _connection.StartAsync();
                    };
    
                    _connection.On<string, string>("ReceiveMessage", (user, message) =>
                    {
                        string fullMessage = $"{user}: {message}";
                        MessageEvent?.Invoke(this, fullMessage);
                    });
                }
                try
                {
                    await _connection.StartAsync();
                }
                catch (Exception ex)
                {
                    MessageEvent?.Invoke(this, $"{ex.Message}; base Exception: {ex.GetBaseException().Message}");
                    await Task.Delay(new Random().Next(0, 5) * 1000);
                    await Connect(serverIp, port);
                }
            }
    
            public async Task SendMessage(string user, string message)
            {
                try
                {
                    await _connection.InvokeAsync("ProcessClientMessage", user, message);
                }
                catch (Exception ex)
                {
                    MessageEvent?.Invoke(this, ex.Message);
                }
            }
        }
    }
    

就是这样。希望这对您有所帮助。


2

它说你可以自行托管,并且有在Windows服务中执行此操作的示例,但是它们并不适用于.NET Core。 - NibblyPig
2
ASP.NET Core 默认是自托管的。有关 Windows 服务的文档提到可以将 Web 应用程序作为服务运行。https://learn.microsoft.com/aspnet/core/host-and-deploy/windows-service?view=aspnetcore-5.0&tabs=visual-studio#sdk - Brennan
我并不是很理解,也许用.NET做这件事情是不可能的。我会创建一个网站,然后想办法将消息转发到Windows服务中。谢谢你的建议。 - NibblyPig

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接