【问题标题】:Asynchronous task pending in custom action filter?自定义操作过滤器中的异步任务挂起?
【发布时间】:2017-06-03 01:37:49
【问题描述】:

我需要你的帮助!我已经有一段时间了,但我一直无法弄清楚。假设我们有以下控制器:

class NotifierController : MyBaseController {
    ... 
    [HttpGet]
    [CanNotifyUser]
    public async Task<ActionResult> NotifyUser(string id)
    {
        return View(await DataBase.Users.FindAsync(id));
    }
    // More actions decorated with the CanNotifyUserAttribute attribute
}

DataBaseMyBaseController 中定义的属性,如:

static ApplicationDbContext _appDbContext;
public ApplicationDbContext DataBase 
{ 
    get { return _appDbContext ?? (_appDbContext = new ApplicationDbContext()); } 
}

ApplicationDbContext 继承自 IdentityDbContext&lt;MyUser&gt;,其中 MyUser 继承自 IdentityUser。所以,我的问题(我认为)来自自定义操作过滤器CanNotifyUserAttribute,其OnActionExecuting 覆盖定义为(删除所有检查以尽可能减少):

public override async void OnActionExecuting(ActionExecutingContext filterContext)
{
    NotifierController controller = filterContext.Controller as NotifierController;
    Boss boss = await controller.DataBase.Bosses.FindAsync(filterContext.HttpContext.User.Identity.GetUserId());
    object id = filterContext.ActionParameters["id"];
    User user = await controller.DataBase.Users.FindAsync(id.ToString());
    if (user.Department.Id != boss.Department.Id)
    {
        filterContext.Result = new RedirectToRouteResult(
            new RouteValueDictionary(
                new
                {
                    action = "Login",
                    controller = "Account",
                    returnUrl = controller.Url.Action("NotifyUser", new { id = id })
                }));
    }
    base.OnActionExecuting(filterContext);
}

使用此属性的想法是验证老板是否仅向其部门的用户发送通知。现在出现了奇怪的部分,这仅在我第一次尝试向用户发送通知时发生(无论它是否属于同一部门),抛出的错误是:

[InvalidOperationException:异步模块或处理程序已完成,而异步操作仍处于挂起状态。]

如果我尝试(在错误之后)相同的请求(即通知用户),它会像重定向或发送通知一样工作。

根据this post,我可能会在异步任务完成之前完成请求(如果我理解得很好),但我一直在使用的所有任务都是awaited 所以我不知道这个在哪里错误可能来自。希望您能够帮助我。提前致谢!

【问题讨论】:

  • 由于您实现了Fire and forget 模式,因此行为是预期的......现在您实际上期望这段代码会做什么?
  • @AlexeiLevenkov hmmm 那么我应该在NotifyUser 操作中执行task.Wait() 吗?我在属性中尝试过,但没有运气。
  • 不行。当它死锁时,请务必阅读stackoverflow.com/questions/13140523/…。感觉就像您希望操作过滤器支持异步操作,但事实并非如此(视图也是如此)......我认为如果没有重大的重新设计,您将无法取得任何进展,因为只有 ASP.Net MVC 中支持异步的顶级操作。跨度>
  • 是的,根本问题是这个版本的 ASP.NET/MVC 在操作过滤器中的异步操作方面存在可悲的缺陷。 ASP.NET/WebApi 在这方面方式更好,但并不能真正帮助您。
  • 另见我的article on async ASP.NET,我注意到这个错误通常是由于async void(即async void OnActionExecuting),还有一个关于“当前异步支持状态”的部分。跨度>

标签: c# asp.net-mvc asynchronous


【解决方案1】:

您正试图通过将 OnActionExecuting 转换为 async void 来对过滤器强制执行异步行为。 asnyc void 方法只能在异步事件处理程序的上下文中使用,因为它们具有特殊的错误处理语义以及调用进程“无法”等待在您的 (filter-) 方法体内执行的任务(供进一步阅读,请考虑 this Stephen Cleary 的精彩文章。

因此,对于 ASP.NET Core,请考虑改用 IAsyncActionFilter 接口。

过滤器通过不同的接口定义支持同步和异步实现。 [...] 异步过滤器定义了一个 OnStageExecutionAsync 方法。此方法采用 FilterTypeExecutionDelegate 委托,该委托执行过滤器的管道阶段。 (Filters in ASP.NET Core)

所以不是...

public override async void OnActionExecuting(ActionExecutingContext filterContext)

...使用:

public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)

有关如何使用此功能的更多说明,请参阅:Async OnActionExecuting in ASP.NET Core's ActionFilterAttribute

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-23
    • 2017-03-14
    • 1970-01-01
    • 2012-09-11
    • 1970-01-01
    • 1970-01-01
    • 2018-04-11
    • 1970-01-01
    相关资源
    最近更新 更多