Проблема с подключением SignalR клиент-сервер

#c# #asp.net #.net #asp.net-core #blazor

#c# #asp.net #.net #asp.net-core #блейзор

Вопрос:

Я пытался выяснить проблему в течение всего дня и не мог ее понять. Это мой концентратор SignalR (BlazorServerAppHub.cs)

     using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.SignalR;
    using BlazorServerApp.Data;
    
    namespace BlazorServerApp
    {
        public class BlazorServerAppHub: Hub
        {
            public const string HubUrl = "/chat";
      
            public async Task Broadcast(WeatherForecast[] forecasts)
            {
                await Clients.All.SendAsync("ReceiveMessage", forecasts);
            }
    
            public override Task OnConnectedAsync()
            {
                return base.OnConnectedAsync();
            }
        }
    }
 

Это страница бизнес-логики (WeatherForecastService.cs)

 using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using BlazorServerApp.Data;
using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Timers;

namespace BlazorServerApp.Data
{
    public class WeatherForecastService  
    {
        private readonly IHubContext<BlazorServerAppHub> _hubContext;

        public WeatherForecastService(IHubContext<BlazorServerAppHub> hubContext)
        {
            _hubContext = hubContext;
        }
        
        protected  async Task Broadcast()
        {
            var forecasts = await GetForecastAsync();
            await _hubContext.Clients.All.SendAsync("Broadcast", forecasts);
        }

        public Task<WeatherForecast[]> GetForecastAsync()
        {
            var rng = new Random();
        }    
                
    }

 public class WeatherForecast
    {
        public int A { get; set; }

    }
}
 

Наконец, это моя страница razor (fetchData.razor)

 @page "/fetchdata"

@using BlazorServerApp.Data;
@inject NavigationManager navigationManager
@using Microsoft.AspNetCore.SignalR.Client;

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from a service.</p>

@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
else
{

    <table class="table">
        <thead>
            <tr>
                <th>A</th>
               
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.A</td>
                    
                </tr>
            }
        </tbody>
    </table>
}

@code {

    private  WeatherForecast[] forecasts;
    private HubConnection _hubConnection;

    protected override async Task OnInitializedAsync()
    {
        string baseUri = navigationManager.BaseUri;
        string hubUrl = baseUri.TrimEnd('/')   BlazorServerAppHub.HubUrl;

        _hubConnection = new HubConnectionBuilder().WithUrl(hubUrl).Build();
        _hubConnection.On< WeatherForecast[]>("Broadcast", forcast => { forecasts = forcast; StateHasChanged(); });

        await _hubConnection.StartAsync();
    }
}
 

По сути, я пытаюсь получить данные с сервера и периодически передавать их клиенту. Проблема в том, что моя страница, когда я запускаю приложение forecasts в fetchData.razor, пуста, поэтому на странице отображается «Загрузка». Почему это так? Я предполагаю, что я что-то упускаю в сообщении SignalR.

Комментарии:

1. Настройка концентратора / соединения кажется нормальной. Как вы используете / вызываете WeatherForecastService для периодической отправки данных?

2. Сначала я хочу показать один пример. Убедитесь, что каждое клиентское соединение может видеть одни и те же данные (поскольку я генерирую их случайным образом). Затем я добавлю эту логику (все еще не уверен на стороне клиента или на стороне сервера). Проблема в том, что он не работает даже для одного образца.

3. Это имеет смысл. Вам все равно нужно вызвать WeatherForecastService.Broadcast() сервер, чтобы отправить сообщение клиентам. Вы это где-то делаете?

4. По-видимому, это то, чего мне не хватает. Где я мог бы это назвать? Мне нужно, чтобы это запускалось при открытии страницы?

Ответ №1:

Для того чтобы сервер периодически передавал данные клиенту, вам необходимо запустить какую-то фоновую службу. Есть несколько способов сделать это, например:

Однако, поскольку вы сказали, что просто хотите отправить данные один раз в целях тестирования, вы можете подключиться Hub.OnConnectedAsync() , чтобы отправить данные клиенту при первоначальном подключении. Во-первых, я бы предложил создать отдельный интерфейс / репозиторий для Task<WeatherForecast[]> GetForecastAsync() :

 public interface IWeatherForecastRepository
{
    Task<WeatherForecast[]> GetForecastAsync();
}

public class WeatherForecastRepository
{
    private static readonly string[] Tickers = new[]
    {
        "10", "20", "30", "44", "77"
    };

    private static readonly int[] Ones = new[]
    {
        1000, 15000, 7000, 500, 2200
    };

    public Task<WeatherForecast[]> GetForecastAsync()
    {
        var rng = new Random();
        return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            A = index,
            B = Tickers[index - 1],
            C = NextFloat(rng),
            D = Ones[index - 1],
            E = rng.Next(0, 10000),
        }).ToArray());
    }

    static float NextFloat(Random random)
    {
        double decimalPart = random.NextDouble();
        double intPart = random.Next(0, 1000);

        return (float)Math.Round(intPart   decimalPart, 3); ;
    }
}
 

Затем, как только вы зарегистрируете этот репозиторий с помощью DI, вы можете внедрить его в свой BlazorServerAppHub и использовать в OnConnectedAsync() :

 public class BlazorServerAppHub : Hub
{
    public const string HubUrl = "/chat";

    private readonly IWeatherForecastRepository _weatherForecastRepository;

    public BlazorServerAppHub(IWeatherForecastRepository weatherForecastRepository)
    {
        _weatherForecastRepository = weatherForecastRepository;
    }

    public async Task Broadcast(WeatherForecast[] forecasts)
    {
        await Clients.All.SendAsync("ReceiveMessage", forecasts);
    }

    public override async Task OnConnectedAsync()
    {
        await base.OnConnectedAsync();

        var forecasts = await _weatherForecastRepository.GetForecastAsync();

        // Clients.Caller will only send the data to the client that just connected
        await Clients.Caller.SendAsync("Broadcast", forecasts);
    }
}
 

Комментарии:

1. @Gray_Rhino без проблем. Код кажется достаточно общим, чтобы все было в порядке, но, конечно, не стесняйтесь удалять его, если нет