WCF (C#), calling an Async method. help! - c#

I'm stumped with this and would appreciate any help at all!
I'm calling the Amazon api using WCF and the Visual Studio generated -asynch- methods.
There's a WPF page, with a button on. Press the button and it calls the search method in another class. (see code below)
In this other searcher class, I add the method AmazonItemSearchCompleted to handle the ItemSearchCompleted event. Then I call the asynch search function from the generated wcf code.
Client.ItemSearchCompleted += AmazonItemSearchCompleted;
Client.ItemSearchAsync(itemSearch);
This all seems to work fine. But the AmazonItemSearchCompleted method only seems to get hit after all the code in the calling form ends, ie. when I'm stepping though (no matter how long I wait for the service to respond), it gets hit at the final bracket after searchAmazon(). But by this time, it's too late to use the result of the request!!
private void button1_Click(object sender, RoutedEventArgs e)
{
searchAmazon();
} // <----- AmazonItemSearchCompleted get's hit here
private void searchAmazon()
{
var AzSearch = new AmazonSearch();
var ISBNS = new List<string>();
ISBNS.Add("0439023513");
//ISBNS.Add("9780071374323");
AzSearch.GetBookNameFromISBN(ISBNS[0]);
}
Maybe I'm missing something here, but I have no idea why the event seems to fire late?
Should I abandon the asynch methods and use the synchronous ones with a background worker?? (maybe more straightforward?)
Thanks for any help or pointers you can offer!

That's the whole point of async methods. You fire them and the code returns immediately to avoid blocking the UI until the service responds. You use the result only in the success callback (AmazonItemSearchCompleted). In the case of a WPF application if you use async methods you should be aware that the success callback could be invoked on a thread which is different than the main GUI thread and in which you should not update the controls. You need to use the Dispatcher object.

Related

Delegate method not being run on UI thread

I am building an Windows Form-based app that listens on a port and when it receives a specific command, it opens a window (Form) that requests input.
My problem is that even though I am using a delegated method to open the window, only the window furniture/border is drawn. The contents of the form are not rendered at all.
From searching other answers on S.O., there seem to be two causes for this:
InitializeComponent() not being called
Trying to open the window from a non-UI thread
It appears that #2 is my problem. When I compare the ManagedThreadId in the form constructor and from the callback delegate, they are different.
As far as I can tell from the docs, the delegate should ensure that the callback is run on the UI thread.
Can anyone suggest why it isn't?
Below is a simplified version of what my code looks like.
The form
public wPrompt(bool silent, bool listen)
{
InitializeComponent();
// Thread.CurrentThread.ManagedThreadId returns 1
// Register a handler for scan requests received via the network
SocketListener.OpenFormRequest += OpenFormCommandHandler;
// Class that contains the code to open a socket and listen for commands
SocketListener.StartListening();
}
private void wPrompt_Load(object sender, System.EventArgs e)
{
// Thread.CurrentThread.ManagedThreadId returns 3
}
// The callback that gets called by the delegate
private void OpenFormCommandHandler()
{
// Thread.CurrentThread.ManagedThreadId returns 3
// Open the form
Visible = true;
}
The SocketListener class
class SocketListener
{
public delegate void OpenFormRequestEventHandler();
public static event OpenFormRequestEventHandler OpenFormRequest;
public static void StartListening()
{
// Thread.CurrentThread.ManagedThreadId returns 1
// Initialise and start worker thread
workerThread = new Thread(new ThreadStart(ListenThread));
workerThread.Start();
}
// A slightly modified version of the Synchronous Server Socket Example at
// https://learn.microsoft.com/en-us/dotnet/framework/network-programming/synchronous-server-socket-example
private static void ListenThread()
{
// Thread.CurrentThread.ManagedThreadId returns 3
// Opens socket and listens for command
if (/* command received */) {
OpenFormRequest?.Invoke();
}
}
}
The delegate callback function is run on the same thread as the socket listener. Not the UI thread as expected.
Can anyone explain what's happening please? I don't do a lot of .net development, so I am having trouble nutting this out.
As far as I can tell from the docs, the delegate should ensure that the callback is run on the UI thread. Can anyone suggest why it isn't?
There are no docs that should suggest that. I.e. it's not sufficient simply to use a delegate. You have to invoke the delegate using a mechanism that would move that invocation onto the UI thread, and there's nothing like that in the code you posted above. You seem to have misunderstood whatever it was that you read.
The issue with your code is that you appear to have confused the compiler-generated Invoke() method for a delegate with the framework-provided Control.Invoke() method. Your code calls the former, while you should be calling the latter. All that the former does is to actually invoke the delegate; the latter is what handles marshaling the execution of a delegate onto the UI thread so it can be executed there.
Frankly, it's a mistake for the socket-related code to try to address this at all. In the ListenThread() method, just raise the event normally (which ironically is the syntax you're using, so actually you don't need to change anything there). In your OpenFormCommandHandler() method, then you should call the Control.Invoke() method to execute whatever code you need to execute there, such as creating and showing a new form.
Based on your recent edit, in theory here is how you would change your event handler:
private void OpenFormCommandHandler()
{
// Thread.CurrentThread.ManagedThreadId returns 3
// Open the form
this.Invoke((MethodInvoker)(() => Visible = true;));
}
But I infer from your problem description that the form has not actually been shown once yet, which means it hasn't yet been tied to main thread and so Control.Invoke() is unlikely to work (you'd probably get an exception reporting that the window handle hadn't been created yet…I forget the exact wording, and it's not important enough for me to go looking it up right now).
Assuming that's the case, you need to get a synchronization context from elsewhere. Unfortunately, the question still lacks the specifics that would allow a more explicit answer showing exactly how to do that. But depending on what else is going on in your program, you could:
Pass a different Form instance to the wPrompt constructor and use that instance when calling Invoke(). Or,
Pass SynchronizationContext.Current to the wPrompt constructor, and call that object's Send() or Post() method (equivalent to Control.Invoke() and Control.BeginInvoke(), respectively).
There are other mechanisms you could use to capture and then use the synchronization context, but I'd say based on the details in your question, one of those two will be preferable to you.

Is there an easy way to simulate events between WCF service and WCF client?

So I've achieved localhost WCF Named Pipes communication between client EXE and server EXE. I can call class methods on the server over localhost. So, it's like an IPC/RPC. However, if the server's class method takes a long time to execute, then it's best for me to throw that into a thread so that the server class method finishes and runs this thread in the background. Okay, fine, but then when the thread is finished its long task, I want to alert the client without having to use a timer on the client that would check that class method. A timer hitting a class method is a lot more inefficient than a raised event. It's like I need to raise an event on the client from the server. Is there an easy way to do this or to at least simulate it, without a lot of confusing work?
This is an answer formulated from my comment to the OP's question
You could make your WCF methods asynchonous then it's a simple matter of async/await or do away with WCF completely and use built-in async with NamedPipeClientStream (which is still await compatible). Not to mention a speed boost in the latter when doing away with verbose XML SOAP encoding
OP:
#MickyD You were right on the async/await thing now that I have studied that and implemented a test that works. That allows me to almost simulate a callback on a long running task and with minimal lines of code
e.g. to build upon the OP's answer but to use async/await correctly:
Client code
private async void button1_Click(object sender, EventArgs e) // <-- note async
{
label1.Text = await client.GetDataAsync(textBox1.Text); // <-- note await. New method
}
Now you could be tempted to use Task.Run but doing so is bad because:
Task.Run is best suited for compute-bound operations which we aren't.
Task.Run will at the most use an expensive thread-pool thread
We're performing an I/O operation and as such can benefit from I/O Completion Ports and "there is no thread" philosphy of IOCP present in Task I/O bound operations. As such when we make the server call via GetDataAsync, we don't waste a thread waiting for a result.
WCF Server
Here we simulate a lengthy operation by waiting, but instead of using Sleep which isn't Task-aware, we use Task.Delay which is an awaitable operation.
Task<string> async GetDataAsync (string text)
{
await Task.Delay (Timespan.FromSeconds(5));
return text + " processed";
}
So, let's say you have a button click in your UI that does a WCF synchronous method call on the WCF service, and that synchronous method takes a long time to run. Obviously, you don't want to block the UI from updating while that long running task executes. Naturally, you might be thinking about a callback. As in, you make the call to the server, and the class method on the server spawns a thread and runs that task, and when it's done, it returns back to the client a result via a callback.
To set that all up on WCF involves many complex, confusing, poorly documented steps, actually. But there's a much easier way, and it doesn't involve WCF code at all, and doesn't involve you changing anything on the WCF service, nor editing any WCF configurations. The trick is introduced in .NET 4.5 and greater. It's called async and await. Here is an example of a button click that calls a WCF service method that takes a long time to run and then returns the result when it's finished, and yet the GUI doesn't lock up and can handle other events.
1. First, to simulate a slow task, edit your WCF service project's shared class method and add this line in before the return result so that you can simulate a 5 second pause:
Thread.Sleep(5000); // requires using System.Threading;
In my case, I put that in my GetData() method.
2. Now switch to your WCF client project. You may have a button click handler that looks like this, as an example:
private void button1_Click(object sender, EventArgs e)
{
string returnString = client.GetData(textBox1.Text));
label1.Text = returnString;
}
So, switch that with three minor changes:
a. Add using System.Threading.Tasks;.
b. Change private void... to private async void... on your button click handler.
c. Utilize await Task.Run(...) with your slow method call.
Thus, the code would look like so:
private async void button1_Click(object sender, EventArgs e)
{
// Task.Run() requires "using System.Threading.Tasks;"
string returnString = await Task.Run(() => client.GetData(textBox1.Text));
label1.Text = returnString;
}
The end result is that when you click the button in the WCF client project, the GetData() class method is called on the WCF service project in a background thread, and, when finished, it comes back to that await statement and returns the result to the variable assignment. In my case, I clicked the button and nothing happened for 5 seconds -- the label with the result string didn't change. However, the GUI wasn't locked up -- I could drag the window around, type in other fields, click other form buttons, and so on. So, it's almost like a callback event handler, but not exactly. Still, it serves the same functionality and can be used in place of a callback event handler in most cases. And it involves far less code.

WPF Method to wait for DocumentCompleted Event

I know this has been asked before, but I don't think these solutions are flexible. The DocumentCompleted event should be used to determine when the load has completed, not as a method for performing work. If you need to perform several different tasks that each have to navigate several times, placing the logic in the DocumentCompleted event turns it into a messy switch/case router that is hard to read and maintain.
You need something that can actually wait during your method performing navigation so you can continue your task in the method you are already in. My first though is an actual Wait() method.
I would think something like this is close:
void WaitForLoad()
{
isLoading = true;
while (isLoading)
{
if (Application.Current == null) break;
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, (DispatcherOperationCallback)delegate(object unused) { return null; }, null);
}
}
And set Isloading to false in the DocumentCompleted event.
You should be able to just call this method after whatever action will cause a pageload. It works, it has some issues.
1) it sends the CPU usage for the app up to 35% until the page has loaded, even if nothing else is happening.
2) if the application tries to close while its running, the loop will keep running and leave the app open with no windows, hence the need for the break when the app is null.
Can this be fixed, or am I coming at this all the wrong way?
Edit: I tried implementing the ManualResetEvent solution below, but it led to several other issues that I am not sure can be resolved without creating a messier situation than the one above. Since the WebBrowser is on the UI, locking the thread stop the entire app. If the work is done on the background thread it can be locked, but then accessing the WebBrowser becomes very difficult.
In your situation, it sounds like you want a specific thread to block while waiting for the document to load. In that case, you would do something like this:
protected ManualResetEvent _resetEvent = new ManualResetEvent(false);
public void WaitingThread()
{
_resetEvent.WaitOne();
// Do stuff after the web browser completes.
}
public void LoadWebPage()
{
webBrowser.Navigate(new Uri(url));
webBrowser.DocumentCompleted = (s, e) => { _resetEvent.Set(); };
}
Basically, when the document completes, you signal the event and any threads waiting on the event unblock and continue executing.
I noticed that you use Dispatcher.CurrentDispatcher.Invoke this is good for calling your method that somehow updates UI from another thread. But from code provided, I don't see any code in other thread then UI. So
Run that code on another thread.
On the close event of your application you can make isLoading=false; And more, if the method invoked is kind of long running stuff insert
if(!isLoading)
return;
//or in some other app suitable way break an execution
EDIT:
Even better way to handle this in multithreading, then just simply relay on boolean variable, is using some Synchonization object

Silverlight and problems with async call

I have some code that works as follows:
App.xaml calls a SetUp() method which populates a local collection using async calls and exposes the collection as a public property.
That's all good.
Now I make an instance of the first page in my Silverlight app like so
private void Application_Startup(object sender, StartupEventArgs e)
{
this.RootVisual = new Summary();
}
In the constructor of Summary() I expect those async calls to have been complete and my collection to be filled but the async calls have not yet complete. Even if I do a Thread.Sleep(100000....) before i make an instance on Summary() this is the case
And the thing is that until the constructor of Summary() is exited and the UI displayed to the user my async calls do not get kicked off!
What!!!
Is there anything I can do about that or is that just the way asyn calls work i.e. they wait until the current until of work finished before firing?
This is how I work round this situation (I'll use simple string download as an example):-
private void Application_Startup(object sender, StartupEventArgs e)
{
WebClient web = new WebClient();
web.DownloadStringCompleted += (s, args) =>
{
// Do stuff with args.Result);
this.RootVisual = new Summary();
};
web.DownloadStringAsync(new Uri("SomeUrl.txt", UriKind.Relative));
}
Note that the construction of Summary and the assignment to RootVisual are defered until the asynchronous download is complete.
Silverlight was specifically designed to not have any blocking operations -- that's why you have to do async in the first place. What you really have to do is make your app run properly while waiting for the async completion handler to execute.
This is by design. The alternative would be that the user has to wait longer before he sees anything at all.
If you really want to prevent showing an incomplete summery then first shown another page with 'waiting' or a progress bar. Fire the async events from that page or its parent. Then show the Summary when the async call returns.
To deal with an incomplete Summary you might want to use ICommand
I know this is an old thread, but for all following reader i want to provide my experience with a similar problem. If i understood you correctly - the reason why your async calls do not complete is because you block the main thread. I ran into the same problem, if you block the main thread none of the other threads continue. The solution was to do the async calls inside a backgroundworker and show, like pauldendulk said, a waiting - page. Use Debug.WriteLine() to monitor the process in the output.

Synchronizing events based one com objects

I am using API that originally was written with native code and wrapped with .net interops. The API is work asynchronic way when each operation raises event when it finished.
All my logic is synchronic so I want to synchronizing the operations. I doing it with EventWaitHandle. here the code
Stock stock;
private System.Threading.EventWaitHandle _signal = null;
public void Sync()
{
_signal = new System.Threading.EventWaitHandle(false,
System.Threading.EventResetMode.AutoReset);
MBTradingProvider.Instance.FinnishGetStoch += new
EventHandler(Instance_FinnishGetStoch);
MBTradingProvider.Instance.GetStockAsync("IBM");
_signal.WaitOne();
}
void Instance_FinnishGetStoch(object sender, EventArgs e)
{
stock = MBTradingProvider.Instance.CurrentWorkongStock;
if (_signal != null)
_signal.Set();
}
This code stuck in the _signal.WaitOne() line, the current thread is freezes and nothing to be happened.
I worked the same pattern on some other async operation and I work fine. The only difference that I can think about is that under the hood works com objects, as I said the effect that I get is that the code not responding after the WaitOne line
Anyone have an idea what can be wrong?
One issue with this code is the assignment of the _signal variable. This variable is atomically set but it is not necessarily visible between all threads involved. You need to use a method like Interlocked.Exchange to ensure the set is visible at the same time in all threads.
System.Threading.EventWaitHandle temp = new System.Threading.EventWaitHandle(false,
System.Threading.EventResetMode.AutoReset);
Interolocked.Exchange(ref _signal, temp);
Also, why are you not using an AutoReset event directly?
Your code seems to be fine to me.
I am assuming the following:
Sync() is executed in ThreadA
Calling GetStockAsync() will create ThreadB, which will perform some task to get stock info
Upon completing to get the stock info, ThreadB, not ThreadA, will execute the event handler, Instance_FinnishGetStoch(),
My rough guess is that the assumption#3 may not be true for your async framework.
You may want to try your code asynchronously and check which thread executes GetStockAsyc() and which thread executes Instance_FinishGetStock() event handler.
If only one thread does both GetStockAsync() and Instance_FinishGetStock(), your code above will be stuck on _signal.WaitOne().
For example, in a winform, if Form.BeginInoke(MyDelegate) is called in the UI thread, MyDelegate will be executed in the same UI thread, yet still asynchronously.

Categories