WPF + Tasks + WCF = No SynchronizationContext? - wpf

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.
The WCF proxy is generated using ChannelFactory, and uses wsHttpBinding. There is no callback contract. The relevant code is shown below:
private TaskScheduler _uiScheduler;
public MainWindow()
{
InitializeComponent();
_uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var serviceTask = new Task<Int32>(ServiceCallWrapper,
CancellationToken.None,
TaskCreationOptions.None);
var continueTask = serviceTask.ContinueWith(result => 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 }
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.
One note - the SynchronizationContext is not permanently lost - if I Click on the button again, the button click handler does have the correct SynchronizationContext.
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:
Fails - Calls WCF Service
WpfContinuationsTest.MainWindow.ServiceContinuation
WpfContinuationsTest.MainWindow.<Button_Click>b__0
System.Threading.Tasks.Task`1+<>c__DisplayClass17.<ContinueWith>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
Succeeds - No Call To WCF Service
WpfContinuationsTest.MainWindow.ServiceContinuation
WpfContinuationsTest.MainWindow.<Button_Click>b__0
System.Threading.Tasks.Task`1+<>c__DisplayClass17.<ContinueWith>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
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?

According to Microsoft, this is a known bug with the TPL:
http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/629d5524-c8db-466f-bc27-0ced11b441ba

Related

How to ensure wcf service client finishs his works in silverlight?

I use wcf service client to submit changes of data for a silverlight project. The correlative codes like this:
public class DispatcherCollection : UpdatableCollection<DocumentDispatcher>
{
public override void SubmitChanges()
{
DocumentServiceClient client = new DocumentServiceClient();
client.NewDocumentCompleted += (s, e) =>
{
// (s as DocumentServiceClient).CloseAsync();
// do something
};
client.UpdateColumnCompleted += (s, e) =>
{
// (s as DocumentServiceClient).CloseAsync();
// do something
};
client.RemoveDocumentCompleted += (s, e) =>
{
// (s as DocumentServiceClient).CloseAsync();
// do something
};
foreach (DocumentDispatcher d in this)
{
if (d.IsNew)
{
// d=>object[] data
client.NewDocumentAsync(data);
d.IsNew=false;
}
else
{
foreach (string propertyName in d.modifiedProperties)
{
client.UpdateColumnAsync(d.ID, GetPropertyValue(propertyName));
}
dd.ClearModifications();
}
}
foreach (DocumentDispatcher dd in removedItems)
{
client.RemoveDocumentAsync(dd.ID);
}
removedItems.Clear();
}
}
Class UpdatableCollection derives from ObserableCollection, and I implemtent logics in class DocumentDispatcher and UpdatableCollection to buffer the changes of data such as new created, property modified and removed. I use SubmitChanges method to submit all changes to server.
Now I am stuck:
1. I am at a loss when to close the client after a bunlde fo async calls. I don't know which callback is the last one.
2. What will happen when a user closes the IE immediately right after clicking the save button (it seems to be done because it runs async but in fact the updating threads are industriously running.)?
You can keep a counter or use an isbusy function to monitor the callbacks from your Async calls - to make sure they all finished.
If the user fires off a request to the WCF service, the WCF service will complete but there will be no call back - as the application will be closed.
I think that there is no wait handle for silverlight asynchornized call brings inconvenience. Here is my experence. I want to check and submit modifications of data which are not expicitly submitted when browser is closing. I have implemented codes in App_Exit like this:
private void Application_Exit(object sender, EventArgs e)
{
Document doc = EDPViewModel.CurrentViewModel.Document;
if (doc != null) new ServiceClient().SubmitChangesAsync(doc);
}
provided that in the SubmitChangesAsync method, not submitted modifications of doc are found out and submitted. Therefore, because of the asynchronized running features, while the service invoking is being sent, the application is yet immediately closed. And that will dispose related resouces of the application, including Service Invoking Tasks. So the codes above work not. I hope so eagerly that somewhere exists a mechanism, which can export a wait handle from silverlight asynchronized call, so that I can update the above codes whith this:
private void Application_Exit(object sender, EventArgs e)
{
Document doc = EDPViewModel.CurrentViewModel.Document;
if (doc != null)
{
Task t = new TaskFactory().StartNew(() => new ServiceClient().SubmitChangesAsync(doc));
t.Wait();
}
}
With wait operation I can really be sure that all modifications are really definitely submitted. So is there any similar pattern that can be used in silverlight?
It's for me a good news, as you put it, that calls could work like the mode "requesting and forgetting". So I needn' to worry too much about data losing during submitting.
To ensure all service calls are sent out before application is closed, I think, counter is a simple and effient idea. I will try to implement it in my project.
Thank you for your help!

MessageBox.Show early in App startup causes app to terminate

As part of my App's startup procedure, it checks data integrity, and if it finds a problem it pops up a message to the user telling them that it might take a while to repair things.
I'm showing the message using MessageBox.Show. Because the data check is done from a worker thread, I'm switching over to the UI thread to make that call, and then setting a ManualResetEvent to tell the worker thread when the user has acknowledged the message.
I kick off the data check/load very early in the app's lifecycle from the constructor in the main Application class, by spinning off a worker thread (using the ThreadPool).
When I run with the debugger, and the message is displayed, the app just waits for input. When I run without the debugger, the app terminates after displaying the dialog for 10 seconds.
That 10 seconds is a big clue - it tells me that the OS thinks the app took too long to initialize (the OS kills apps that take too long to start up).
I think that my MessageBox.Show is blocking the UI thread before the App.RootFrameNavigating has a chance to be invoked.
My questions:
Does my diagnosis sound right?
I'd prefer to kick off my data load early, because it is almost entirely IO, except for this Message Box, and the sooner I can get my Model loaded, the better, but do you normally delay your data load until later in the app lifecycle?
Any other ideas/suggestions? I can't guarantee which page will be the start page, because the app could be resuming to any page. I'm also thinking of having the MessageBox.Show delay itself until the app has initialized, perhaps polling away for a flag set by App.RootFrameNavigating - does that make sense?
I think your problem is a result of kicking off the worker thread in the Application constructor. You should use the appropriate life-cycle event, in this case: PhoneApplicationService.Activated Event
So, the solution I've come up with is to still kick off the data load in a worker-thread from the Application's constructor, but in my PhoneService's class ShowDialog method that I invoke to invoke MessageBox.Show, I check to see if the initial navigation has occurred:
private readonly ManualResetEvent _appInitialized = new ManualResetEvent(false);
public void AppInitialized()
{
_appInitialized.Set();
}
public void ShowDialog(string caption, string text, Action<MessageBoxResult> callback, MessageBoxButton button = MessageBoxButton.OKCancel)
{
_appInitialized.WaitOne();
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
var result = MessageBox.Show(text, caption, button);
if (callback != null)
{
callback(result);
}
});
}
Then in my Application class:
private bool _firstNavigate = true;
private void RootFrameNavigating(object sender, NavigatingCancelEventArgs e)
{
if (_firstNavigate)
{
_firstNavigate = false;
var navigationService = (NavigationService) sender;
navigationService.Navigated += NavigationServiceNavigated;
}
....
private void NavigationServiceNavigated(object sender, NavigationEventArgs e)
{
var navigationService = (NavigationService)sender;
navigationService.Navigated -= NavigationServiceNavigated;
PhoneServices.Current.AppInitialized();
}
Anyone see any issues with this approach? Anyone come up with a better way?

Silverlight Socket.ConnectAsync method never calls the Completed event

Silverlight's Socket.ConnectAsync method never calls the Completed event - I am using 127.0.0.1:4510 . I have configured IIS to serve clientaccesspolicy.xml file on port 943.
I am using following code:
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
//args.UserToken = tcp;
args.RemoteEndPoint = ep;
args.Completed += new EventHandler<SocketAsyncEventArgs>(OnConnect);
mrEvent.Reset();
bool x = tcp.ConnectAsync(args);
mrEvent.WaitOne();
private void OnConnect(object sender, SocketAsyncEventArgs e)
{
isConnected = (e.SocketError == SocketError.Success);
mrEvent.Set();
}
The OnConnect method is never called and as a result the code blocks due to the WaitOne method.
Note: This code is being executing on a background thread so there is no deadlock due to the WaitOne call.
I had a similar issue I just resolved; I saw your post when I was looking for answers and thought I'd best share, even though it's an old post. Even if you're long past this issue, others will find this post.
I thought my event handler wasn't firing, too. Turns out it WAS, but it was firing on a background thread and couldn't affect the user interface. The solution was to make sure that the handler fired on the user interface thread. Use CheckAccess and, if it's not on the main thread, send it there using Dispatcher.BeginInvoke.
Hope this helps!
Silverlight async socket API (the only one available) is very bad.
In your case, if the value, returned from ConnectAsync is false, the call was made synchronous and the OnConnect method was not called.
Here is the synchronous version of the ConnectAsync:
public static bool Connect(this Socket socket, EndPoint remoteAddress) {
ManualResetEvent semaphore = new ManualResetEvent(false);
SocketAsyncEventArgs socketEventArgs = new SocketAsyncEventArgs() {
RemoteEndPoint = remoteAddress
};
socketEventArg.SetBuffer(buffer, 0, buffer.Length);
socketEventArg.Completed += (s, e) => {
semaphore.Set();
};
semaphore.Reset();
bool wasAsynchronous = socket.ConnectAsync(socketEventArgs);
if (wasAsynchronous) {
semaphore.WaitOne();
}
return socketEventArgs.SocketError == SocketError.Success;
}

Calling a webservice synchronously from a Silverlight 3 application?

I am trying to reuse some .NET code that performs some calls to a data-access-layer type service. I have managed to package up both the input to the method and the output from the method, but unfortunately the service is called from inside code that I really don't want to rewrite in order to be asynchronous.
Unfortunately, the webservice code generated in Silverlight only produces asynchronous methods, so I was wondering if anyone had working code that managed to work around this?
Note: I don't need to execute the main code path here on the UI thread, but the code in question will expect that calls it makes to the data access layers are synchronous in nature, but the entire job can be mainly executing on a background thread.
I tried the recipe found here: The Easy Way To Synchronously Call WCF Services In Silverlight, but unfortunately it times out and never completes the call.
Or rather, what seems to happen is that the completed event handler is called, but only after the method returns. I am suspecting that the event handler is called from a dispatcher or similar, and since I'm blocking the main thread here, it never completes until the code is actually back into the GUI loop.
Or something like that.
Here's my own version that I wrote before I found the above recipe, but it suffers from the same problem:
public static object ExecuteRequestOnServer(Type dalInterfaceType, string methodName, object[] arguments)
{
string securityToken = "DUMMYTOKEN";
string input = "DUMMYINPUT";
object result = null;
Exception resultException = null;
object evtLock = new object();
var evt = new System.Threading.ManualResetEvent(false);
try
{
var client = new MinGatServices.DataAccessLayerServiceSoapClient();
client.ExecuteRequestCompleted += (s, e) =>
{
resultException = e.Error;
result = e.Result;
lock (evtLock)
{
if (evt != null)
evt.Set();
}
};
client.ExecuteRequestAsync(securityToken, input);
try
{
var didComplete = evt.WaitOne(10000);
if (!didComplete)
throw new TimeoutException("A data access layer web service request timed out (" + dalInterfaceType.Name + "." + methodName + ")");
}
finally
{
client.CloseAsync();
}
}
finally
{
lock (evtLock)
{
evt.Close();
evt = null;
}
}
if (resultException != null)
throw resultException;
else
return result;
}
Basically, both recipes does this:
Set up a ManualResetEvent
Hook into the Completed event
The event handler grabs the result from the service call, and signals the event
The main thread now starts the web service call asynchronously
It then waits for the event to become signalled
However, the event handler is not called until the method above has returned, hence my code that checks for evt != null and such, to avoid TargetInvocationException from killing my program after the method has timed out.
Does anyone know:
... if it is possible at all in Silverlight 3
... what I have done wrong above?
I suspect that the MinGatServices thingy is trying to be helpful by ensuring the ExecuteRequestCompleted is dispatched on the main UI thread.
I also suspect that your code is already executing on the main UI thread which you have blocked. Never block the UI thread in Silverlight, if you need to block the UI use something like the BusyIndicator control.
The knee-jerk answer is "code asynchronously" but that doesn't satisfy your question's requirement.
One possible solution that may be less troublesome is to start the whole chunk of code from whatever user action invokes it on a different thread, using say the BackgroundWorker.
Of course the MinGatServices might be ensuring the callback occurs on the same thread that executed ExecuteRequestAsync in which case you'll need to get that to run on a different thread (jumping back to the UI thread would be acceptable):-
Deployment.Current.Dispatcher.BeginInvoke(() => client.ExecuteRequestAsync(securityToken, input));

Asynchronous Callback method is never called to give results from web service from Silverlight

I'm calling off asynchronously to a web service (Amazon Web Services) from a Silverlight app and my callback method is never actually triggered after I start the asynchronous call.
I've set up another web service proxy in a console app, and I'm able to make a synchronous call and get a response using the same arguments without any issues.
Am I possibly having problems with the fact that this is called from within a browser? I'm not sure where to start, since I don't get a response at all, much less an error.
Below is the code I'm using:
private void btnQueryAmazon_Click(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrEmpty(txtQuery.Text))
{
ItemSearch search = new ItemSearch();
/// set authentication and search parameters
AmazonService.AWSECommerceServicePortTypeClient service = new AmazonService.AWSECommerceServicePortTypeClient();
service.ItemLookupCompleted += new EventHandler<AmazonService.ItemLookupCompletedEventArgs>(service_ItemLookupCompleted);
service.ItemSearchAsync(search);
}
}
void service_ItemLookupCompleted(object sender, AmazonService.ItemLookupCompletedEventArgs e)
{
txtError.Text = e.Result.Items.Count().ToString();
grdItems.ItemsSource = e.Result.Items;
}
Well, there's your problem ;)
It looks like you're calling the ItemSearch method on the service, but you're wiring up and handling the ItemLookup method.
I do it all the time.

Resources