Что происходит с CultureInfo, когда InvokeAsyn c (StateHasChanged) в компоненте Blazor запускается событием из внешней службы? - PullRequest
0 голосов
/ 08 апреля 2020

Проблема, связанная с Внешний вызов методов компонента для обновления состояния .

Итак, у меня есть компонент, который должен отображать данные, полученные через обработчик событий. Событие инициируется службой, которая зарегистрирована как одиночная. Это событие вызывается, когда метод-концентратор SignalR вызывается из клиента.

Компонент, который отображает данные, поддерживает несколько CulturInfo. Теперь проблема в том, что CulturInfo, с помощью которого компонент перерисовывается при получении новых данных, является CultureInfo потока, который инициирует событие в одноэлементном сервисе.

Некоторый код и подробное описание:

Каждый клиент может отправить информацию о здоровье, вызвав ReceiveHealthInfo(HealthModel healthInfo) в концентраторе SignalR. Это вызовет метод HealthService.UpdateStatusInfo(string monitorId, HealthModel info). HealthService вызовет событие StatusUpdated. Компонент прослушивает это событие, обрабатывает данные и вызывает StateHasChanged асинхронно при возникновении события. Теперь компонент обновляет страницу с обновленными данными. И страница отображается с CultureInfo потока, который вызвал событие StatusUpdated.

Почему это происходит? Вызов InvokeAsync(StateHasChanged) в компоненте не является действием, отправляемым потоку, которому принадлежит компонент. Не следует ли компоненту повторно выполнить рендеринг с помощью CultureInfo, с помощью которого компонент отображался в первый раз?

При форсировании CurrentUICulture в HealthService компонент выполняет визуализацию с этим принудительным CurrentUICulture.

Я что-то здесь не так ? Заранее большое спасибо.

Компонент: HealthMonitor.razor.cs

sealed public partial class HealthMonitor : IDisposable
{
    private List<HealthModel> data { get; set; } = new List<HealthModel>();

    [Inject]
    private HealthService healthService { get; set; }
    [Inject]
    private IStringLocalizer<HealthMonitor> stringLocalizer { get; set; }

    protected override Task OnInitializedAsync()
    {
        healthService.StatusUpdated += HealthService_StatusUpdated;
        return base.OnInitializedAsync();
    }

    private async Task HealthService_StatusUpdated(HealthModel[] healthModel)
    {
        await InvokeAsync(() =>
        {
            data = healthModel.ToList();
            StateHasChanged();
        });
    }

    public void Dispose()
    {
        healthService.StatusUpdated -= HealthService_StatusUpdated;
    }
}

Singleton service: HealthService

internal class HealthService
{
    public event Func<HealthModel[], Task> StatusUpdated;

    public HealthStates State { get; private set; }
    public ConcurrentDictionary<string, HealthModel> HealthInfo { get; private set; } = new ConcurrentDictionary<string, HealthModel>();
    private readonly ILogger<HealthService> logger;

    public HealthService(ILogger<HealthService> logger)
    {
        this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    public async Task UpdateStatusInfo(string monitorId, HealthModel info)
    {
        if (info == null)
            return;
        HealthInfo[monitorId] = info;
        await UpdateHealthState();
    }

    private async Task UpdateHealthState()
    {
        // Some enum mapping to get a global state
        if (HealthInfo.Values.All(h => h.State == MonitorStates.ServiceOk))
            State = HealthStates.Ok;
        else if (HealthInfo.Values.Any(h => h.State == MonitorStates.ServiceWarning))
            State = HealthStates.Warning;
        else if (HealthInfo.Values.Any(h => h.State == MonitorStates.ServiceError))
            State = HealthStates.Error;
        else
            State = HealthStates.Undefined;

        // If the following line is uncommented, the component is rendered in Dutch, 
        // otherwise in the CurrentUICulture of the thread executing this method. 
        //Thread.CurrentThread.CurrentUICulture = new CultureInfo("nl");

        if (StatusUpdated != null)
        {
            await StatusUpdated.Invoke(HealthInfo.Values.ToArray());                
        }
    }
}

Концентратор SignalR

public class HealthHub : Hub<IMonitorClient>
{
    private readonly HealthService healthService;

    public HealthHub(HealthService healthService)
    {
        this.healthService = healthService ?? throw new ArgumentNullException(nameof(healthService));
    }

    public async Task ReceiveHealthInfo(HealthModel healthInfo)
    {
        await healthService.UpdateStatusInfo(Context.ConnectionId, healthInfo);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...