首页 > 解决方案 > 返回 302 重定向的异步控制器操作被下一个请求“取代”

问题描述

我们正在构建一个 ASP.Net Core 3.1 应用程序。它包含一个页面、一个工作项目列表和一个允许将某些项目分配给某人的表单。如果控制器动作成功,它以重定向到再次显示工作项的动作结束。

它注意到有时列表会重新加载一些具有旧分配的项目,然后刷新然后获取更新的列表。

列表的控制器 GET 操作是异步的,等待获取项目列表的代码,以及一些嵌套代码以使用 Linq to Entities 查询从数据库获取数据ToListAsync()

public async Task<IActionResult> Index()
{
    _log.Debug("Getting work items");
    List<Item> model = await _itemData.GetItems();
    return View(model);
}

控制器 POST 操作也是异步的,但没有任何等待。

public async Task<IActionResult> Index(int employeeId, int[] itemIds)
{
    _itemData.Assign(employeeId, itemIds);

    _log.Debug("Redirecting to work items");
    return RedirectToAction(nameof(Index));
}

但是,被调用的代码包含这样的结构:

public bool Assign(int employeeId, int[] itemIds)
{
    _log.Debug("Start assigning items");
    Array.ForEach(itemIds, async itemId =>
    {
        _log.Debug($"Done assigning item {itemId}");
        using (var scope = _serviceScopeFactory.CreateScope())
        {
            var db = scope.ServiceProvider.GetService<context>();

            Item item = await db.Items.Where(i => i.Id == itemId).FirstOrDefaultAsync();
            item .EmployeeId= employeeId;
            await db.SaveChangesAsync();
            _log.Debug($"Done assigning item {itemId}");
        }
    });

    _log.Debug("Done assigning items");
    return true;
}

我已经简化了很多代码,希望我没有引入与此不一致的地方......

我想知道这实际上是如何工作的。

async使用指令处理循环。此代码 awaitFirstOrDefaultAsync()SaveChangesAsync(),但仅在循环内。日志显示该方法本身在所有等待的任务完成之前返回。

日志

Getting work items
Start assigning items
Start assigning item 1234
Start assigning report 2345
Done assigning item
Redirecting to work items
Getting work items
Done assigning item 1234
Done assigning item 2345

这是预期的行为吗?

我试图使Assign()方法异步并等待它的调用,但正如我所料,这并没有改变结果。

我可以结合异步分配并以故障安全的方式等待完整的结果,还是最好简单地更改为完全同步的解决方案?

标签: c#async-awaitasp.net-core-3.1

解决方案


您的Assign方法需要是异步的:

public async Task<bool> Assign(int employeeId, int[] itemIds)
{
    _log.Debug("Start assigning items");

    foreach (var itemId in itemIds)
    {
        _log.Debug($"Done assigning item {itemId}");
        using (var scope = _serviceScopeFactory.CreateScope())
        {
            var db = scope.ServiceProvider.GetService<context>();

            Item item = await db.Items
                .Where(i => i.Id == itemId)
                .FirstOrDefaultAsync();
            item .EmployeeId= employeeId;
            await db.SaveChangesAsync();
            _log.Debug($"Done assigning item {itemId}");
        }
    }

    _log.Debug("Done assigning items");
    return true;
}

推荐阅读