Async method hanging in asp.net

2019-08-29 09:56发布

Environment: ASP.NET MVC 5, SQL Server

Here is my method that returns current user's profile from the database:

public static ProfileModel getCurrentProfile(HttpContextBase ctx) {
   User user = AccountController.getUser(ctx);

   Task<ProfileModel> model = ProfileModel.getValue(user.UserID);
   model.Wait();
   return model.Result;
}

Upon execution, model.Wait() just hangs up.

I have read several articles about deadlocks and using ConfigAwait(false) for such situations. However, there would be lot of places I would need to call this method. I am thinking if I fix it the right way, I may be able to avoid ConfigAwait() calls altogether.

Here is how I am using the method in my index.cshtml file:

Members.Models.ProfileModel  userModel = HomeController.getCurrentProfile(Context);
Html.RenderPartial("_PartialPublicProfile", userModel);

File _PartialPublicProfile requires ProfileModel instance to be passed in. Is it possible to pass in Task<ProfileModel> instance as a parameter?

Or, is there a better way to solve the problem? Regards.

标签: asp.net-mvc
1条回答
放我归山
2楼-- · 2019-08-29 10:15

You're essentially trying to run an async task synchronously. You have to be very careful about how you do that or else your application can and will hang.

In Entity Framework 6, there are now sync and async data access methods, but importantly, the sync versions call the async versions synchronously. To pull this off the EF team uses the following internal helper class. I would recommend implementing this as your own helper class, and then using it in all scenarios where you need to call an asynchronous method synchronously:

public static class AsyncHelper
{
    private static readonly TaskFactory _myTaskFactory = new
      TaskFactory(CancellationToken.None,
                  TaskCreationOptions.None,
                  TaskContinuationOptions.None,
                  TaskScheduler.Default);

    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
    {
        return AsyncHelper._myTaskFactory
          .StartNew<Task<TResult>>(func)
          .Unwrap<TResult>()
          .GetAwaiter()
          .GetResult();
    }

    public static void RunSync(Func<Task> func)
    {
        AsyncHelper._myTaskFactory
          .StartNew<Task>(func)
          .Unwrap()
          .GetAwaiter()
          .GetResult();
    }
}

So, in your particular scenario here, you code would change to:

public static ProfileModel getCurrentProfile(HttpContextBase ctx){
   User user = AccountController.getUser(ctx);

   var model = AsyncHelper.RunSync<ProfileModel>(() => ProfileModel.getValue(user.UserID));
   return model;
}
查看更多
登录 后发表回答