您可以在Razor页面中使用IAsyncEnumerable逐步显示标记吗?

时间:2019-09-10 12:22:05

标签: razor-pages blazor asp.net-core-3.0 c#-8.0 iasyncenumerable

我一直在使用Blazor和C#8.0中的IAsyncEnumerable功能。是否可以使用IAsyncEnumerable并在Razor页面中等待以逐步显示带有数据的标记?

示例服务:

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'运算符只能在异步方法中使用。考虑使用“异步”修饰符标记该方法,并将其返回类型更改为“任务”。

有什么可能的想法吗?

2 个答案:

答案 0 :(得分:5)

服务器端Razor允许您描述。 This video描述了this Github repo中的代码,该代码展示了如何通过修改服务器端Blazor模板中的<givenName>JOHN</givenName> <surname>DOE</surname> <givenName>JANE</givenName> <surname>DOE</surname> 示例来使用IAsyncEnumerable。

修改服务本身很容易,实际上可以产生更简洁的代码:

ForecastService

另一方面,“ Blazor”页面更为复杂。不仅仅是循环必须在显示HTML之前完成,您不能在页面本身中使用 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)] }; } } 是因为它不是异步的。您只能在代码块中定义异步方法。

您可以做的是枚举IAsyncEnumerable,并在每次更改后通知页面刷新自身。

渲染代码本身不需要更改:

await foreach

<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()

在问题的示例中,传入的游戏可以存储在列表中,而渲染代码保持不变:

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

答案 1 :(得分:3)

您无法在await foreach模板代码上编写.razor。但是,作为解决方法,您可以在@code部分中编写它:

@if (@gamesUI == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Game</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var game in gamesUI)  // <--- workaround
            {
                <tr>
                    <td>@game</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    List<string> gamesUI;  // <--- workaround

    protected override async Task OnInitializedAsync()
    {
        gamesUI = new List<string>();
        await foreach(var game in GService.GetgamesAsync() )
        {
            gamesUI.Add(game);
            this.StateHasChanged();
        }
    }
}

效果:

enter image description here

产量数据:

        private static readonly string[] games = new[] { "Call of Duty", "Legend of Zelda", "Super Mario 64", "Bag man" };


        public async IAsyncEnumerable<string> GetgamesAsync()
        {
            var rng = new Random();

            foreach (var game in games)
            {
                await Task.Delay(1000);
                yield return game;
            }
        }