ContinueWith loses the SynchronizationContext
asp.net, asp.net-mvc, async-await, c#, c#-5.0
Solution
In order to force scheduling of the continuation on the request context thread, you need to specify the `TaskScheduler` that should be used when scheduling the continuation.
public async Task<ActionResult> Index()
{
Log("before GetAsync");
await new HttpClient().GetAsync("http://www.example.com/")
.ContinueWith(request =>
{
Log("ContinueWith");
request.Result.EnsureSuccessStatusCode();
},
TaskContinuationOptions.AttachedToParent,
CancellationToken.None,
TaskScheduler.FromCurrentSynchronizationContext());
return View();
}
Howver, you are using `await` which automatically marshals continuations on to the current `SynchronizationContext`. You should be able to do this:
public async Task<ActionResult> Index()
{
Log("before GetAsync");
HttpResponseMessage request = await new HttpClient().GetAsync("http://www.example.com/");
//everything below here is you 'continuation' on the request context
Log("ContinueWith");
request.EnsureSuccessStatusCode();
return View();
}
Problem
In the snippet below, the `SynchronizationContext` is lost, and because of that also the `CurrentCulture` and `CurrentUICulture`. `Log()` comes from this answer. ``` public async Task<ActionResult> Index() { Log("before GetAsync"); await new HttpClient().GetAsync("http://www.example.com/") .ContinueWith(request => { Log("ContinueWith"); request.Result.EnsureSuccessStatusCode(); }, TaskContinuationOptions.AttachedToParent); return View(); } static void Log(string message) { var ctx = System.Threading.SynchronizationContext.Current; System.Diagnostics.Debug.Print("{0}; thread: {1}, context: {2}, culture: {3}, uiculture: {4}", message, System.Threading.Thread.CurrentThread.ManagedThreadId, ctx != null ? ctx.GetType().Name : String.Empty, System.Threading.Thread.CurrentThread.CurrentCulture.Name, System.Threading.Thread.CurrentThread.CurrentUICulture.Name); } ``` This is the output: before GetAsync; thread: 56, context: AspNetSynchronizationContext, culture: nl, uiculture: nl ContinueWith; thread: 46, context: , culture: nl-BE, uiculture: en-US Before the `GetAsync`, the culture and UI culture have the values I set in `Application_BeginRequest`. Inside the `ContinueWith`, the context is missing, the culture is set to what's provided by the browser, and UI culture is set to some default. From what I understand, everything with `AspNetSynchronizationContext` should be happening automatically. What's wrong with my code?