Null exception running async method synchronously with MS AsyncHelper
asp.net-mvc, asynchronous, c#, razor, synchronous
Solution
I have been searching for the simplest way to run async tasks from a non-async method.
This has been discussed many times, and there is no solution that works in every scenario. The internal `AsyncHelper` type is used only in very specific situations where the ASP.NET team knows it's safe; it's not a general-purpose solution.
Generally, the approaches are:
- Block (using `Result` or `GetAwaiter().GetResult()`). This approach can cause deadlocks (as I describe on my blog) unless you consistently use `ConfigureAwait(false)` - and all of the code you call also consistently uses `ConfigureAwait(false)`. However, note that your code can't use `ConfigureAwait(false)` unless it doesn't actually need the ASP.NET context.
- Nested message loop. This is possible using something like `AsyncContext` from my AsyncEx library. However, there are a lot of ASP.NET APIs that implicitly assume the current `SynchronizationContext` is `AspNetSynchronizationContext`, which is not the case within `AsyncContext`.
- Separate thread with blocking (using `Task.Run(...).GetAwaiter().GetResult()`). This approach avoids the deadlocks you can see with just blocking, but it does execute the code outside of an ASP.NET context. This approach also negatively impacts your scalability (which is the whole point of using `async` on ASP.NET in the first place).
In other words, these are just hacks.
ASP.NET vNext has the concept of "view components" which may be `async`, so this will be a natural solution in the future. For today's code, IMO the best solution is to make the methods synchronous; that's better than implementing a hack.
Problem
As you cannot run async methods from child window (`@Html.Action`) calls, I have been searching for the simplest way to run async tasks from a non-async method. This will allow my `MainMenu` controller `Menu` action to still work when injected like this (rather than have to move to a VM or Ajax solution): ``` <div class="row"> @Html.Action("MainMenu", "Menu") </div> ``` I tried this promising approach using a copy of the code MS use themselves: How to call asynchronous method from synchronous method in C#? AsyncHelper code: ``` 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(); } } ``` and I am consuming it like this: ``` public async Task<ActionResult> MainMenu() { if (_currentCandidate == null) { throw new ArgumentNullException("_currentCandidate"); } var candidateId = AsyncHelper.RunSync<int>(() => _currentCandidate.CandidateIdAsync()); [snip] } ``` Which calls this async method: ``` public async Task<int> CandidateIdAsync() { var applicationUser = await this.ApplicationUserAsync(); if (applicationUser != null) { return applicationUser.CandidateId.GetValueOrDefault(); } return 0; } ``` Only when I run this I get the following error: What am I missing here? The code looks like it should work, but I am not familiar enough with it yet to figure it out. Update: For reference the `MainMenu` controller class looks like this: ``` public class MenuController : Controller { readonly ICurrentCandidate _currentCandidate; public MenuController(ICurrentCandidate currentCandidate) { _currentCandidate = currentCandidate; } // GET: MainMenu public ActionResult MainMenu() { if (_currentCandidate == null) { throw new ArgumentNullException("_currentCandidate"); } var candidateId = AsyncHelper.RunSync<int>(() => _currentCandidate.CandidateIdAsync()); [snip] return View(vm); } } ``` Another update: The failure appears to be inside the related IF code as a simplified `CandidateIdAsnyc` works: ``` // This works public async Task<int> CandidateIdAsync() { return 0; } ``` Here is the rest of that code: ``` public class CurrentCandidate : ICurrentCandidate { private readonly ApplicationDbContext _applicationDbContext; private readonly IApplicationUserManager _userManager; private readonly ICandidateStore _candidateStore; public CurrentCandidate(ApplicationDbContext applicationDbContext, ICandidateStore candidateStore, IApplicationUserManager userManager) { this._candidateStore = candidateStore; this._applicationDbContext = applicationDbContext; this._userManager = userManager; // new ApplicationUserManager(new UserStore<ApplicationUser>(this._applicationDbContext)); } public async Task<ApplicationUser> ApplicationUserAsync() { var applicationUser = await this._userManager.FindByIdAsync(HttpContext.Current.User.Identity.GetUserId()); return applicationUser; } public bool IsAuthenticated() { return HttpContext.Current.User.Identity.IsAuthenticated; } public async Task<int> CandidateIdAsync() { var applicationUser = await this.ApplicationUserAsync(); if (applicationUser != null) { return applicationUser.CandidateId.GetValueOrDefault(); } return 0; } } ```