问题描述
我一直在使用Blazor和C#8.0中的IAsyncEnumerable功能.是否可以使用IAsyncEnumerable并在Razor页面中等待以逐步显示带有数据的标记?
I've been playing around with Blazor and the IAsyncEnumerable feature in C# 8.0. Is it possible to use IAsyncEnumerable and await within Razor Pages to progressively display markup with data?
示例服务:
private static readonly string[] games = new[] { "Call of Duty", "Legend of Zelda", "Super Mario 64" };
public async IAsyncEnumerable<string> GetGames()
{
foreach (var game in games)
{
await Task.Delay(1000);
yield return game;
}
}
剃须刀页面中的示例:
@await foreach(var game in GameService.GetGames())
{
<p>@game</p>
}
这将导致错误CS4033:'await'运算符只能在异步方法中使用.考虑使用异步"修饰符标记此方法,并将其返回类型更改为任务".
This gives error CS4033: 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'.
有什么可能的想法吗?
推荐答案
服务器端Razor允许您描述. 此视频描述了此Github存储库,其中显示了如何通过修改服务器端Blazor模板中的ForecastService
示例来使用IAsyncEnumerable.
Server-side Razor allows what you describe. This video describes the code in this Github repo that shows how to use IAsyncEnumerable by modifying the ForecastService
example in server-side Blazor template.
修改服务本身很容易,实际上可以产生更简洁的代码:
Modifying the service itself is easy, and actually results in cleaner code :
public async IAsyncEnumerable<WeatherForecast> GetForecastAsync(DateTime startDate)
{
var rng = new Random();
for(int i=0;i<5;i++)
{
await Task.Delay(200);
yield return new WeatherForecast
{
Date = startDate.AddDays(i),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
};
}
}
另一方面,"Blazor"页面更为复杂.这不仅是循环必须在显示HTML之前完成,而且您不能在页面本身中使用await foreach
,因为它不是异步的.您只能在代码块中定义异步方法.
The Blazor page on the other hand is more complicated. It's not just that the loop would have to finish before the HTML was displayed, you can't use await foreach
in the page itself because it's not asynchronous. You can only define asynchronous methods in the code block.
您可以做的是枚举IAsyncEnumerable,并在每次更改后通知页面刷新自身.
What you can do, is enumerate the IAsyncEnumerable and notify the page to refresh itself after each change.
渲染代码本身不需要更改:
The rendering code itself doesn't need to change :
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
OnInitializeAsync
收到每个项目后需要调用StateHasChanged()
:
OnInitializeAsync
needs to call StateHasChanged()
after receiving each item :
List<WeatherForecast> forecasts;
protected override async Task OnInitializedAsync()
{
forecasts =new List<WeatherForecast>();
await foreach(var forecast in ForecastService.GetForecastAsync(DateTime.Now))
{
forecasts.Add(forecast);
this.StateHasChanged();
}
}
在问题的示例中,传入的游戏可以存储在列表中,而渲染代码保持不变:
In the question's example, incoming games could be stored in a List, leaving the rendering code unchanged :
@foreach(var game in games)
{
<p>@game</p>
}
@code {
List<string> games;
protected override async Task OnInitializedAsync()
{
games =new List<games>();
await foreach(var game in GameService.GetGamesAsync())
{
games.Add(game);
this.StateHasChanged();
}
}
}
这篇关于您可以在Razor页面中使用IAsyncEnumerable逐步显示标记吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!