【问题标题】:Using Hub from separate class in ASP.NET Core 3在 ASP.NET Core 3 中从单独的类中使用 Hub
【发布时间】:2019-10-24 05:15:24
【问题描述】:

我正在开发一个程序,在该程序中我从 SignalR 接收数据,执行处理,然后在处理完成后将 SignalR 消息发送回客户端。我找到了一个 couple of resources 来了解如何做到这一点,但我不太清楚如何在我的项目中实现它。

我的代码如下所示:

引导


    public static void Main(string[] args)
    {
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        List<ISystem> systems = new List<ISystem>
        {
            new FirstProcessingSystem(),
            new SecondProcessingSystem(),
        };
        Processor processor = new Processor(
            cancellationToken: cancellationTokenSource.Token,
            systems: systems);
        processor.Start();
        CreateHostBuilder(args).Build().Run();
        cancellationTokenSource.Cancel();
    }

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

    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSignalR();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<TestHub>("/testHub");
            });
        }
    }

TestHub.cs


    public class TestHub : Hub
    {
        public async Task DoStuff(Work work)
        {
            FirstProcessingSystem.ItemsToProcess.Add(work);
        }
    }

工作.cs


    public class Work
    {
        public readonly string ConnectionId;
        public readonly string Data;

        public Work(string connectionId, string data)
        {
            ConnectionId = connectionId;
            Data = data;
        }
    }

处理器.cs

    public class Processor
    {
        readonly CancellationToken CancellationToken;
        readonly List<ISystem> Systems;

        public Processor(
            CancellationToken cancellationToken,
            List<ISystem> systems)
        {
            CancellationToken = cancellationToken;
            Systems = systems;
        }

        public void Start()
        {
            Task.Run(() =>
            {
                while (!CancellationToken.IsCancellationRequested)
                {
                    foreach (var s in Systems)
                        s.Process();
                }
            });
        }
    }

系统


    public interface ISystem
    {
        void Process();
    }

    public class FirstProcessingSystem : ISystem
    {
        public static ConcurrentBag<Work> ItemsToProcess = new ConcurrentBag<Work>();

        public void Process()
        {
            while (!ItemsToProcess.IsEmpty)
            {
                Work work;
                if (ItemsToProcess.TryTake(out work))
                {
                    // Do things...
                    SecondProcessingSystem.ItemsToProcess.Add(work);
                }
            }
        }
    }

    public class SecondProcessingSystem : ISystem
    {
        public static ConcurrentBag<Work> ItemsToProcess = new ConcurrentBag<Work>();

        public void Process()
        {
            while (!ItemsToProcess.IsEmpty)
            {
                Work work;
                if (ItemsToProcess.TryTake(out work))
                {
                    // Do more things...
                    // Hub.Send(work.ConnectionId, "Finished");
                }
            }
        }
    }

我知道我可以在 Hub 中执行处理,然后发回“已完成”调用,但我想将我的处理与入站消息分离,这样我可以在需要时添加更多 ISystems

有人可以请这个吗? (另外,如果有人有更好的方法来构建我的程序,我也很感激反馈)

【问题讨论】:

    标签: c# asp.net-core .net-core signalr-hub asp.net-core-signalr


    【解决方案1】:

    aspnet有一个非常强大的依赖注入系统,你为什么不用呢?通过在没有依赖注入的情况下创建工作服务,您将很难使用 aspnet 提供的任何东西。

    由于您的“处理系统”似乎是长时间运行的服务,您通常会让它们实现 IHostedService,然后创建一个通用服务启动器 (taken from here):

    public class BackgroundServiceStarter<T> : IHostedService where T : IHostedService
    {
        readonly T _backgroundService;
    
        public BackgroundServiceStarter(T backgroundService)
        {
            _backgroundService = backgroundService;
        }
    
        public Task StartAsync(CancellationToken cancellationToken)
        {
            return _backgroundService.StartAsync(cancellationToken);
        }
    
        public Task StopAsync(CancellationToken cancellationToken)
        {
            return _backgroundService.StopAsync(cancellationToken);
        }
    }
    

    然后将它们注册到ConfigureServices中的DI容器:

    // make the classes injectable
    services.AddSingleton<FirstProcessingSystem>();
    services.AddSingleton<SecondProcessingSystem>();
    
    // start them up
    services.AddHostedService<BackgroundServiceStarter<FirstProcessingSystem>>();
    services.AddHostedService<BackgroundServiceStarter<SecondProcessingSystem>>();
    

    现在您已正确设置所有内容,您可以使用 IHubContext&lt;TestHub&gt; context 在需要它的任何类的构造函数参数中简单地注入对您的 signalR 集线器的引用(如您发布的一些链接中所述)。

    【讨论】:

    • 非常感谢您的回复!我对这将如何工作感到有些困惑。实现我的 StartAsync() 和 StopAsync() 重要吗?还是只用于DI?使用您提供的代码,我能够让它工作,但我对 .NET 不够熟悉,无法理解幕后发生的事情。目前我有 StartAsync() 和 StopAsync() 返回 Task.CompletedTask。
    • StartAsyncStopAsync 必须在那里才能满足接口,但是如果在启动和停止时需要做一些工作,您只需要添加实现,您应该可以使用 @987654329 @。正在发生的事情是,DI 容器将创建两个类的单个实例,并在幕后注入 SignalR Hub。 AddHostedService 将这两个类作为长时间运行的服务启动,因此只要您的 WebAPI 还活着,它们就会一直运行。
    • 这是否是说如果我创建我的 ISystems 并将它们 DI 到我的处理器中,同时还将它们添加为 IHostedServices,那么实际上将有两个 ISystems 实例?这是否也意味着如果我的 SecondProcessingSystem 具有 IHubContext DI 并设置为public static IHubContext&lt;TestHub&gt; TestHubContext;,那么如果我在我的 Process() 中使用它可能会导致竞争条件? (即,单例实例设置 SecondProcessingSystem.TestHubContext 而 Process() 尝试使用它?)
    • 如果您将它们DI到您的“流程”中,那么它们应该只有一个实例。尽管我猜你可能不应该使用 ISystems 接口对它们进行 DI。除非您使用服务集合中的实例将最初获得的列表也准备为单例。
    猜你喜欢
    • 1970-01-01
    • 2019-05-28
    • 2021-04-10
    • 2020-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多