2

I need your help! I've been a while around this and I haven't been able to figure it out. Let's suppose we have the following controller:

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 } 

DataBase is a property defined in MyBaseController like:

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

ApplicationDbContext inherits from IdentityDbContext<MyUser> where MyUser inherits from IdentityUser. So, my problem (I think) comes to the custom action filter CanNotifyUserAttribute, whose OnActionExecuting override is defined as (removing all checks to reduce as much as possible):

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

The idea with this attribute is to verify that a boss sends notifications to the users of his department only. Now comes the strange part, this only happens the first time I try to send a notification to an user (no matter whether it's of the same department or not), the error thronw is:

[InvalidOperationException: An asynchronous module or handler completed while an asynchronous operation was still pending.]

If I try (after the error) the same request (i.e., notify the user) it works as expexted either redirecting or sending the notification.

According to this post I might be finishing the request (If I understood well) before an asynchronous task is finished, but all the tasks I've been using are all awaited so I don't know where this error may come from. Hope you can help me. Thanks in advance!

11
  • Since you implemented Fire and forget pattern the behavior is expected... Now what do you actually expect this code would do? Commented Jun 3, 2017 at 1:43
  • @AlexeiLevenkov hmmm So should I do task.Wait() in the NotifyUser action? I tried it in the attribute with no luck. Commented Jun 3, 2017 at 1:51
  • Not going to work. When it deadlocks make sure to read stackoverflow.com/questions/13140523/…. Feels like you expect action filters to support asynchronous operations and it simply not the case (same for views)... I don't think you get anywhere without significant redesign as only top level actions in ASP.Net MVC supporting async. Commented Jun 3, 2017 at 1:52
  • 2
    Yeah, the fundamental problem is that this version of ASP.NET/MVC is tragically flawed when it comes to async operations in action filters. ASP.NET/WebApi is way better in this regard, but doesn't really help you. Commented Jun 3, 2017 at 2:14
  • 1
    Also see my article on async ASP.NET where I note this error is usually due to async void (i.e., async void OnActionExecuting), and also have a section on "Current State of Async Support". Commented Jun 3, 2017 at 3:56

1 Answer 1

2

You are trying to enforce async behaviour on your filter by turning OnActionExecuting into an async void. asnyc void methods should only be used in the context of asynchronous event handlers due to their special error-handling semantics and the "inability" of the calling process to wait for the tasks executed inside your (filter-)method's body (for further reading, consider this great article by Stephen Cleary).

Therefore, with ASP.NET Core, consider using the IAsyncActionFilter interface instead.

Filters support both synchronous and asynchronous implementations through different interface definitions. [...] Asynchronous filters define a single OnStageExecutionAsync method. This method takes a FilterTypeExecutionDelegate delegate which executes the filter's pipeline stage. (Filters in ASP.NET Core)

So instead of...

public override async void OnActionExecuting(ActionExecutingContext filterContext) 

...use:

public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) 

For further instructions on how to use this see: Async OnActionExecuting in ASP.NET Core's ActionFilterAttribute

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.