Я новичок в Blazor, но не в объектно-ориентированных языках.
У меня есть веб-сервис с контроллером, который возвращает запросы из репозитория.
У меня есть независимая служба пользовательского интерфейса, которая вызывает веб-службу.
Я пытаюсь периодически обновлять таблицу с результатами, полученными от веб-сервиса.
@page "/"
@using System.Timers
@using WebServices
@inject StatusService WebService // Has a httpClient to call web service
@inject IConfiguration Configuration
<h4>Status Viewer</h4>
<table class = "table">
<thead>
<tr>
<th>Name</th>
<th>Status</th>
<th>Last Update</th>
</tr>
</thead>
<tbody>
@if (StatusList != null)
{
@foreach (var status in StatusList)
{
<tr>
<td>@status.Name</td>
<td>@status.Status</td>
<td>@status.Timestamp</td>
</tr>
}
}
</tbody>
</table>
@code
{
public IEnumerable<Status> StatusList { get; set; }
private int UIReloadTime;
private const int MINUTE_IN_MILLISECONDS = 60000;
private System.Timers.Timer Timer { get; set; }
protected override async Task OnInitializedAsync()
{
UIReloadTime = int.Parse(Configuration.GetValue<string>("ui:ReloadTime"));
await UpdateStatus();
Timer = new System.Timers.Timer();
Timer.Interval = (UIReloadTime * MINUTE_IN_MILLISECONDS);
Timer.Elapsed += OnElapsed;
Timer.Enabled = true;
Timer.Start();
}
private async void OnElapsed(object? sender, ElapsedEventArgs e)
{
await UpdateStatus();
await InvokeAsync(StateHasChanged);
}
private async Task UpdateStatus()
{
StatusList = await WebService.GetStatus();
}
}
Я получаю допустимое значение во время процесса инициализации, но после этого ничего.
Я знаю, что это как-то связано с повторным рендерингом, но я не знаю, как его запустить, иначе я это делаю сейчас.
Я использую рендеринг на стороне сервера, но не уверен, пытаюсь ли я сделать что-то WASM с рендерингом на сервере.
Я пробовал PeriodicTimer, Threading.Timer и пробовал что-то уже около 2 дней, но ничего не работает.
Я также пытался реализовать IDisposable с типами таймера, но метод Dispose всегда вызывается.
Обновлено: С тех пор я также пытался создать новое приложение WASM и передавать его между попытками. Это все еще не работает. Я официально в тупике
EDIT_2: проблема вызывалась приложением, в котором для параметра «Местоположение интерактивности» было установлено значение для каждой страницы/компонента.
@Lex, лол, у меня снова работает. Я изменил часть кода, сделав его более общим, поскольку он предназначен для работы. Итак, в реальной настройке Status.Status отсутствует.
@Lex Какой шаблон вы использовали при создании примера приложения?
Я использую очень старый испытательный стенд, так было до того, как были выпущены различные интерактивные шаблоны. Просто стандартное приложение .Net 6 Blazor Server.
Шаблон: Веб-приложение Blazor — InteractiveServer — Global Interactivity
Я немного переработал ваш код, чтобы создать минимальный воспроизводимый пример. Я также перешел на использование System.Threading.Timer
и TimeSpan
для обозначения времени.
Мой объект данных и служба.
public record StatusData(string Name, string Status, DateTimeOffset Timestamp);
public class StatusService
{
private List<StatusData> statusData = new List<StatusData>()
{
new("France", "Loaded", DateTimeOffset.Now.AddDays(-3)),
new("Spain", "Loaded", DateTimeOffset.Now.AddDays(-2)),
new("Portugal", "Loaded", DateTimeOffset.Now.AddDays(-1)),
};
public async ValueTask<IEnumerable<StatusData>> GetStatus()
{
// Fake an async API call
statusData.Add(new("Me", "Loading", DateTimeOffset.Now));
await Task.Yield();
return statusData.AsEnumerable();
}
}
И регистрация DI:
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddScoped<StatusService>();
var app = builder.Build();
И моя переформатированная страница:
@page "/"
@using System.Threading
@inject StatusService WebService
@implements IDisposable
<h4>Status Viewer</h4>
<table class = "table">
<thead>
<tr>
<th>Name</th>
<th>Status</th>
<th>Last Update</th>
</tr>
</thead>
<tbody>
@if (StatusList != null)
{
@foreach (var status in StatusList)
{
<tr>
<td>@status.Name</td>
<td>@status.Status</td>
<td>@status.Timestamp</td>
</tr>
}
}
</tbody>
</table>
@code
{
public IEnumerable<StatusData> StatusList { get; set; } = Enumerable.Empty<StatusData>();
private TimeSpan _timeSpan = new TimeSpan(0, 0, 5);
private Timer? _timer { get; set; }
protected override async Task OnInitializedAsync()
{
await UpdateStatus();
// Kick off the first one off timer
_timer = new Timer(this.OnTimerElapsed, null, _timeSpan, Timeout.InfiniteTimeSpan);
}
private async void OnTimerElapsed(object? state)
{
await UpdateStatus();
// Creates a new one off timer once we have the current data
// prevents any thread and data conflicts
_timer = new Timer(this.OnTimerElapsed, null, _timeSpan, Timeout.InfiniteTimeSpan);
await InvokeAsync(StateHasChanged);
}
private async Task UpdateStatus()
{
StatusList = await WebService.GetStatus();
}
public void Dispose()
{
_timer?.Dispose();
}
}
Репо GitHub временно здесь — https://github.com/ShaunCurtis/SO78788856
Я проверил его как есть, и он работает. Я понятия не имею, что на самом деле отличается, поскольку я уже многое из этого попробовал. Попробую это на работе и дам вам знать :)
Вам необходимо предоставить минимально воспроизводимый пример неработающей версии. Проблема часто заключается в тех фрагментах кода, которые вы не показываете в вопросе [потому что не верите в их актуальность].
Итак, это был глобальный интерактивный сервер, который исправил ситуацию. Мне пришлось создать новое приложение и применить все, что я сделал раньше, плюс пару изменений, которые вы внесли сюда, чтобы убедиться, что все настройки установлены правильно. Большое спасибо
Поскольку вы не устанавливали режим рендеринга на странице, я предположил, что вы устанавливаете его глобально.
Я новичок в Blazor, не совсем понял, как это работает. Теперь у меня гораздо больше ясности
Я только что попробовал это в примере приложения, и оно сработало, как и ожидалось. Очевидно, у меня нет вашего веб-сервиса, поэтому я просто смоделировал обновление и добавил туда
await Task.Delay(3000);
, чтобы имитировать небольшую задержку при получении списка. Сначала я подумал, что может возникнуть проблема с вызовомawait InvokeAsync(StateHasChanged)
изнутри методаasync void
, но, похоже, это не проблема. Я тоже в недоумении, почему у тебя это не работает. Меня интересует одна вещь: как можно создать классStatus
со свойствомStatus
? Я не думаю, что компилятор это допустит.