ASP.NET Core SignalR получает доступ к методу Hub из любого места - PullRequest
0 голосов
/ 31 января 2019

Если я потратил много часов на эту проблему, и я нашел много разных стратегий, но ни одна из них не сработала для меня.(Этот код является просто доказательством концепции курса.)

У меня есть следующая настройка с использованием Asp.net core 2.1 (на .Net Framwork 4.7.2):

Я сделал сигнализаторконцентратор, у которого есть метод для отправки числа:

using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;


namespace TestRandomNumberSignalR
{
    public class TestHub : Hub
    {
        public async Task SendRandomNumber(int number)
        {
            await Clients.All.SendAsync("ReceiveRandomBumber", number);
        }
    }
}

Я также создал класс, который обновляет случайное число каждые 3 секунды и добавил его как одиночный:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace TestRandomNumberSignalR
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton(new UpdateRandomNumber());
            services.AddSignalR();
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();
            app.UseSignalR(routes =>
            {
                routes.MapHub<TestHub>("/testHub");
            });
        }
    }
}

Вот класс случайных чисел:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace TestRandomNumberSignalR
{
    public class UpdateRandomNumber
    {
        private bool _continue = true;

        public UpdateRandomNumber()
        {
            var task = new Task(() => RandomNumberLoop(),
                                TaskCreationOptions.LongRunning);
            task.Start();
        }

        private void RandomNumberLoop()
        {
            Random r = new Random();

            while (_continue)
            {
                Thread.Sleep(3000);
                int number = r.Next(0, 100);
                Console.WriteLine("The random number is now " + number);

                // Send new random number to connected subscribers here
                // Something like TestHub.SendRandomNumber(number);

            }
        }

        public void Stop()
        {
            _continue = false;
        }
    }
}

Теперь из этого класса (как я написал в комментарии) я хочу отправить новое случайное число с помощью SignalR.Только как получить там хаб-контекст?

Также я хочу иметь возможность доступа к методу Stop () для класса из контроллера, как я могу получить к нему доступ?

Iсейчас это хорошо обсуждаемая тема, но все равно я нигде не могу найти рабочее решение.Надеюсь, что вы можете помочь мне.

РЕДАКТИРОВАТЬ

Вопрос 1

Пока запускается случайный цикл (большое спасибодо рашараша), еще остались некоторые вопросы.Теперь я не могу ввести правильный UpdateRandomNumber в контроллер.Допустим, я хочу иметь возможность остановить цикл, вызывающий метод UpdateRandomNumber.Stop (), как я могу внедрить синглтон UpdateRandomNumber в контроллер.Я попытался создать интерфейс:

public interface IUpdateRandomNumber
{
    void Stop();
}

Изменение метода RandomNumber для реализации этого:

public class UpdateRandomNumber : IUpdateRandomNumber
{
    private bool _continue = true;

    private IHubContext<TestHub> testHub;

    public UpdateRandomNumber(IHubContext<TestHub> testHub)        
    {
        this.testHub = testHub;

        var task = new Task(() => RandomNumberLoop(),
                            TaskCreationOptions.LongRunning);
        task.Start();
    }

    private void RandomNumberLoop()
    {
        Random r = new Random();

        while (_continue)
        {

            Thread.Sleep(3000);
            int number = r.Next(0, 100);
            Console.WriteLine("The random number is now " + number);

            // Send new random number to connected subscribers here
            // Something like TestHub.SendRandomNumber(number);

        }
    }

    public void Stop()
    {
        _continue = false;
    }
}

И изменение метода add singleton таким образом, чтобы он использовал интерфейс:

        services.AddSingleton<IUpdateRandomNumber>(provider =>
        {
            var hubContext = provider.GetService<IHubContext<TestHub>>();
            var updateRandomNumber = new UpdateRandomNumber(hubContext);
            return updateRandomNumber;
        });

Теперь я могу создать контроллер с методом для остановки цикла randomnumber:

[Route("api/[controller]")]
[ApiController]
public class RandomController : ControllerBase
{
    private readonly IUpdateRandomNumber _updateRandomNumber;

    public RandomController(IUpdateRandomNumber updateRandomNumber)
    {
        _updateRandomNumber = updateRandomNumber;
    }

    // POST api/random
    [HttpPost]
    public void Post()
    {
        _updateRandomNumber.Stop();
    }

Однако эта реализация предотвратит повторный запуск цикла.Итак, как я могу получить доступ к синглтону rondomnumber из контроллера?

Вопрос 2

Из моего класса UpdateRandomNumber я могу теперь вызывать:

testHub.Clients.All.SendAsync("ReceiveRandomBumber", number);

Но почему я сделал метод в моем testhub:

    public async Task SendRandomNumber(int number)
    {
        await Clients.All.SendAsync("ReceiveRandomBumber", number);
    }

Было бы гораздо удобнее создавать методы в хабе, и они вызывали их напрямую.Можно ли это сделать?

1 Ответ

0 голосов
/ 31 января 2019

Вы можете внедрить TestHub в контроллер с помощью Constructor Injection.Так как он уже зарегистрирован в контейнере DI.

public class UpdateRandomNumber
{
    private bool _continue = true;
    private IHubContext<TestHub> testHub;
    private Task randomNumberTask;
    public UpdateRandomNumber(IHubContext<TestHub> testHub)
    {
        this.testHub=testHub;
        randomNumberTask = new Task(() => RandomNumberLoop(),
            TaskCreationOptions.LongRunning);
        randomNumberTask.Start();
    }
    private async void RandomNumberLoop()
    {
        Random r = new Random();

        while (_continue)
        {
            Thread.Sleep(3000);
            int number = r.Next(0, 100);
            Console.WriteLine("The random number is now " + number);

            // Send new random number to connected subscribers here
             await testHub.Clients.All.SendAsync($"ReceiveRandomNumber", number);

        }
    }

    public void Stop()
    {
        _continue = false;
    }
}
public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {

        services.AddSignalR();
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        services.AddSingleton(provider =>
        {
            var hubContext = provider.GetService<IHubContext<TestHub>>();
            var updateRandomNumber = new UpdateRandomNumber(hubContext);
            return updateRandomNumber;
        });

    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        var updateRandonNumber = app.ApplicationServices.GetService<UpdateRandomNumber>();
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseMvc();
        app.UseSignalR(routes =>
        {
            routes.MapHub<TestHub>("/testHub");
        });
    }
}
...