与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);
}
}