当Blazor组件中的InvokeAsync(StateHasChanged)被外部服务的事件触发时,CultureInfo会发生什么?

时间:2020-04-08 09:01:24

标签: asp.net-core blazor blazor-server-side

Invoking component methods externally to update state有关的问题。

因此,我有一个组件应呈现通过事件处理程序接收的数据。该事件是从注册为单例的服务触发的。从客户端调用SignalR集线器方法时将触发此事件。

呈现数据的组件支持多个CulturInfo。现在的问题是,组件在接收到新数据后会重新呈现的CulturInfo是在单例服务中触发事件的线程的CultureInfo。

一些代码和详细说明:

每个客户端都可以通过在SignalR集线器上调用ReceiveHealthInfo(HealthModel healthInfo)来发送健康信息。这将调用方法HealthService.UpdateStatusInfo(string monitorId, HealthModel info)。 HealthService将调用StatusUpdated事件。该组件正在侦听此事件,并将在触发该事件时处理数据并异步调用StateHasChanged。 现在,组件将使用更新的数据重新呈现页面。然后用触发StatusUpdated事件的线程的CultureInfo呈现页面。

为什么会这样?通过在组件中调用InvokeAsync(StateHasChanged),不会将操作分派给拥有该组件的线程。不应使用首次渲染组件的CultureInfo重新渲染组件吗?

通过在HealthService中强制使用CurrentUICulture,该组件将使用此强制的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;
    }
}

单项服务: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);
    }
}

0 个答案:

没有答案