views:

810

answers:

3

In my WPF client, I have a loop that calls a WCF service to update some records. When the loop is done, I display a message, "Update complete".

I'm changing my WCF calls to async calls now.

    ServiceClient client = new ServiceClient();
    client.UpdateRecordsCompleted +=new System.EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(client_UpdateRecordsCompleted);

    foreach (MyItem item in someCollection)
    {
         client.UpdateRecordsAsync(item);
    } 

    MessageBox.Show("Update complete");

I don't need to do anything in the competed event of each operation. I need to just display a message at the end of the last one.

Any ideas?

EDIT: I may be porting this to Silverlight, so that's why I need to call the service asyncronously. I don't think I can use the background worker.

+2  A: 

I would add a thread-safe field to your WPF window to track the number of client updates the user has queued:

private int recordsQueued = 0;

Before dispatching the individual async operations, set recordsQueued to someCollection.Count.

recordsQueued = someCollection.Count;

Finally, in client_UpdateRecordsCompleted, decrement recordsQueued; if it is zero, display the "Update Complete" message:

private void client_UpdateRecordsCompleted(AsyncCompletedEventArgs args) {
  if (Interlocked.Decrement(ref recordsQueued) == 0)
    MessageBox.Show("Update complete.");      
}
Jeff Sternal
volatile won't help here, since you have multiple threads reading and decrementing. Remove volatile, and use interlocked to decrement.
MichaelGG
Blast, of course - this may result in showing the Message Box multiple times.
Jeff Sternal
Actually with volatile, the incorrect behaviour would be that the message would never be shown.
MichaelGG
With volatile, isn't the danger that thread 2 might decrement the value (possibly to zero) after thread 1 does, but before thread 1 executes its test? In that case, wouldn't we see the message twice? Ex:value = 2t1 -> Decrement (value is 1)t2 -> Decrement (value is 0)t1 -> Comparet2 -> Compare
Jeff Sternal
I have a question, where exactly you are closing the client object when doing this? shouldn't you be closing the client object inside "client_UpdateRecordsCompleted" method? I have something similler to this and trying to figureout how client closing should be handled. please advice
Bumble Bee
+2  A: 

If you don't want to overwhelm your server with requests another approach would be to use a background worker, do all your work in a normal loop with synchronous requests. A background worker already has methods for reporting progress and completion which are handy to report back to your main application on the correct thread. Assuming you want to do stuff with your GUI instead of just displaying complete dialog.

Darryl Braaten
Yea that's another good possibility if the workload fits this.
MichaelGG
+2  A: 

Perhaps like this:

ServiceClient client = new ServiceClient();
var count = someCollection.Count;
client.UpdateRecordsCompleted += (_,__) => {
    if (Interlocked.Decrement(ref count) == 0) {
        MessageBox.Show("Update complete.");   
    }
}

foreach (MyItem item in someCollection)
{
     client.UpdateRecordsAsync(item);
}
MichaelGG