Note that there are some explanatory texts on larger screens.

plurals
  1. POWPF + Tasks + WCF = No SynchronizationContext?
    text
    copied!<p>I have a WPF application that is using System.Threading.Tasks to call a WCF service in the background. I'm using Task.ContinueWith to return the results of the service call to the WPF UI thread. My issue is that, although the continuation does run on the UI thread, when it does SynchronizationContext.Current is null. I can run the same code, commenting out the WCF call in the initial Task, and the continuation is on the UI thread, with a DispatcherSynchronizationContext as expected. </p> <p>The WCF proxy is generated using ChannelFactory, and uses wsHttpBinding. There is no callback contract. The relevant code is shown below:</p> <pre><code> private TaskScheduler _uiScheduler; public MainWindow() { InitializeComponent(); _uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); } private void Button_Click(object sender, RoutedEventArgs e) { var serviceTask = new Task&lt;Int32&gt;(ServiceCallWrapper, CancellationToken.None, TaskCreationOptions.None); var continueTask = serviceTask.ContinueWith(result =&gt; ServiceContinuation(result.Result), CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, _uiScheduler); serviceTask.Start(); } private Int32 ServiceCallWrapper() { Int32 result = 0; var service = {elided - initializes service using ChannelFactory }; result = service.TheServiceMethod(); service.Close(); return result; } private void ServiceContinuation(Int32 result) { elided } </code></pre> <p>If I run this code as is, the ServiceContinuation is called on the correct thread (verified using ManagedThreadID), but SynchronizationContext.Current is null. If I comment out the single line that makes the service call (result = service.TheServiceMethod();), then ServiceContinuation is correctly called with a DispatcherSynchronizationContext.</p> <p>One note - the SynchronizationContext is not permanently lost - if I Click on the button again, the button click handler does have the correct SynchronizationContext.</p> <p>I've captured stack traces for the two cases; they have a few differences. I've left out all of the bits that are identical, and only included the top of the stacks where they differ, plus a few frames for reference:</p> <p><strong>Fails - Calls WCF Service</strong></p> <pre><code>WpfContinuationsTest.MainWindow.ServiceContinuation WpfContinuationsTest.MainWindow.&lt;Button_Click&gt;b__0 System.Threading.Tasks.Task`1+&lt;&gt;c__DisplayClass17.&lt;ContinueWith&gt;b__16 System.Threading.Tasks.Task.InnerInvoke System.Threading.Tasks.Task.Execute System.Threading.Tasks.Task.ExecutionContextCallback System.Threading.ExecutionContext.runTryCode System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup System.Threading.ExecutionContext.RunInternal System.Threading.ExecutionContext.Run System.Threading.Tasks.Task.ExecuteWithThreadLocal System.Threading.Tasks.Task.ExecuteEntry System.Threading.Tasks.SynchronizationContextTaskScheduler.PostCallback </code></pre> <p><strong>Succeeds - No Call To WCF Service</strong></p> <pre><code>WpfContinuationsTest.MainWindow.ServiceContinuation WpfContinuationsTest.MainWindow.&lt;Button_Click&gt;b__0 System.Threading.Tasks.Task`1+&lt;&gt;c__DisplayClass17.&lt;ContinueWith&gt;b__16 System.Threading.Tasks.Task.InnerInvoke System.Threading.Tasks.Task.Execute System.Threading.Tasks.Task.ExecutionContextCallback System.Threading.ExecutionContext.Run System.Threading.Tasks.Task.ExecuteWithThreadLocal System.Threading.Tasks.Task.ExecuteEntry System.Threading.Tasks.SynchronizationContextTaskScheduler.PostCallback </code></pre> <p>Does anyone know why, when the only difference is a WCF client service call (with no callback contract), in one case the continuation on the main thread would have a SynchronizationContext, and in the other case it wouldn't?</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload