Я новичок в С# и Blazor, и это моя проблема:
Следующий цикл foreach
очень прост и работает так, как ожидалось.
@if (ClassRoom != null)
{
@foreach (var cr in ClassRoom.value)
{
<p> Classroom name: @cr.name </>
// how to query `cr.url` here ??
}
}
Проблема в том, что внутри цикла foreach мне нужно взять cr.url
и сделать еще один запрос, который, в свою очередь, предоставит мне список учеников в классе.
Точно так же, как я получил ClassRoom
, у меня также есть модель для Students
и сервис, который выбирает студентов как:
Students = await StudentsService.GetStudents(url);
Это означает, что мне не нужна помощь, чтобы привести студентов. Если я даю URL-адрес студентам, то есть GetStudents("http://example.com/students");
, это работает.
Мне нужно, как вызвать его из цикла foreach
Внутри цикла foreach, если я использую
Students = await StudentsService.GetStudents(cr.url)
Я получаю эту ошибку:
The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
Я сейчас отредактирую, но получаю следующую ошибку The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.
Я считаю, что он должен быть дубликатом stackoverflow.com/questions/58358900/… , но у меня нет голосов здесь (обратите внимание, что вы можете перечитать вопрос после редактирования, чтобы убедиться, что минимально воспроизводимый пример размещенный там совпадает с заголовком - я утверждаю, что заголовок не имеет абсолютно никакого отношения к сути вопроса ... но это не мой выбор)
Название этого вопроса должно быть похоже на «Как вызвать асинхронный метод в Blazor». Проблема, с которой вы столкнулись, связана не со свойством url
, а с (асинхронным) методом, который вы пытаетесь вызвать. К сожалению, я никогда не пользовался Blazor, поэтому больше ничем помочь не могу...
@AlexeiLevenkov - Вы правы. Однако здесь проиллюстрирована более широкая проблема — загрузка средства визуализации серверными задачами, которые я попытался решить с помощью ответа ниже.
Вы не должны запускать код извлечения данных в коде рендеринга. Это замедляет визуализацию и делает пользовательский интерфейс невосприимчивым. Как уже указывал @Alexei Levenkov, ссылаясь на более старый ответ, вы не можете запускать асинхронный код как часть процесса рендеринга.
Вместо этого вы должны создать составной совокупный объект в коде компонента (или, в чистом дизайне, в ViewModel/Presenter) и обновлять отображение по мере поступления новых данных.
Вот автономный компонент, демонстрирующий принципы.
я сделал методы данных асинхронными с длительными задержками, чтобы продемонстрировать обновление пользовательского интерфейса по мере поступления каждого нового класса.
@page "/counter"
<PageTitle>Counter</PageTitle>
@foreach (var classItem in _classes)
{
<div class = "m-2 p-2 border border-dark">
<table class = "table-bordered">
<thead>
<tr>
<th>
Name
</th>
</tr>
</thead>
<tbody>
@foreach (var student in classItem.Students)
{
<tr>
<td>
@student.Name
</td>
</tr>
}
</tbody>
</table>
</div>
}
@code {
private readonly List<ClassAggregate> _classes = new List<ClassAggregate>();
protected async override Task OnInitializedAsync()
{
var classes = await GetClasses();
foreach (var classItem in classes)
{
var students = await this.GetStudents(classItem.Uid);
var classAggregate = new ClassAggregate(Class: classItem, Students: students);
_classes.Add(classAggregate);
StateHasChanged();
}
}
private async ValueTask<IEnumerable<ClassRecord>> GetClasses()
{
await Task.Delay(1000);
return new List<ClassRecord>
{
new ClassRecord(Uid:Guid.NewGuid(), Name:"Class 1"),
new ClassRecord(Uid:Guid.NewGuid(), Name:"Class 2"),
new ClassRecord(Uid:Guid.NewGuid(), Name:"Class 3"),
};
}
private async ValueTask<IEnumerable<Student>> GetStudents(Guid classUid)
{
await Task.Delay(2000);
return new List<Student>
{
new Student(Uid:Guid.NewGuid(), ClassUid:classUid, Name:"Student 1"),
new Student(Uid:Guid.NewGuid(), ClassUid:classUid, Name:"Student 2"),
new Student(Uid:Guid.NewGuid(), ClassUid:classUid, Name:"Student 3"),
new Student(Uid:Guid.NewGuid(), ClassUid:classUid, Name:"Student 4"),
new Student(Uid:Guid.NewGuid(), ClassUid:classUid, Name:"Student 5"),
new Student(Uid:Guid.NewGuid(), ClassUid:classUid, Name:"Student 6"),
};
}
public record ClassAggregate(ClassRecord Class, IEnumerable<Student> Students);
public record Student(Guid Uid, Guid ClassUid, string Name);
public record ClassRecord(Guid Uid, string Name);
}
[Вежливо] Подумайте вот о чем: 20 классов, 30 учеников в классе, это 600 записей, отображаемых на очень долго прокручиваемой странице. Это тот UX, который вам нужен? Почему бы не выбрать для класса и показать только один класс?
только один вопрос, потому что код намного больше, чем я ожидал. Если у меня есть ClassRoomModel
и StudentModel
, мне не нужно создавать второй и третий ValueTask
методы, верно? Просто использовать только ClassAggregate
и объединить две модели?
Правильный. Загрузите все в один агрегатный объект.
Я принял этот ответ, потому что вы показываете, как это можно сделать для запроса нескольких зависимых запросов. Но почему-то моя проблема не решена. Не могли бы вы взглянуть на этот похожий вопрос stackoverflow.com/questions/75464937/… Я упростил его, дайте мне знать, если вы можете решить
Я поищу тебя.
var r = await StudentsService.GetStudents(cr.name);
? Не совсем понятно, в чем проблема, с которой вы пишете этот код... Может быть, отредактируйте пост и обязательно покажите, что вы пробовали, и какие именно ошибки (в виде текста) это вызывает? (Предположительно, вы уже старались не переключаться между кодом и HTML слишком часто, поэтому я думаю, что это не та проблема, с которой вы столкнулись)