How do I wait for an asynchronous webmethod in .NET 2.0

Nerseus

Danner
Joined
Oct 22, 2002
Messages
2,547
Location
Arizona, USA
In .NET 1.x, I got back an IAsynchResult (I think) object when I called BeginMethodName. I could hold onto the handle until I really needed the data, then call a Wait method to make sure the webmethod returned (or errored out).

In .NET 2.0, they've implemented the Event-based Asynchronous Pattern. In this pattern, I hook up an event handler and then call MethodNameAsync. My trouble is, I can't figure out how to wait for method to finish from within WinForms.

The scenario is this:
Form1 is a search form that uses a static "Lookups" class. The "Lookups" class exposes a method that makes an asynchronous webmethod call to get some standard lookup tables from the DB.

Form1 launches Form2 some time later. Form2 MUST have the lookups loaded so it needs to wait (or block) until the asynchronouse Lookups class has completed it's webmethod.

I tried adding another static method in my "Lookups" class that goes into a "while(...) Sleep" loop, to wait for the Lookups to complete but that seems to block forever.

What's the best way to use the asynch method and wait until it's done or at least safely check that it's done?

Here's the relevent code:
Code:
partial class Lookups
{
    private static Lookups lookups;
    private static bool gettingLookupsAsynch = false;

    public static void StartLookupsAsync(string wsUrl)
    {
        // If lookups are already in memory, get out
        if (lookups != null) return;

        gettingLookupsAsynch = true;
        WebService webService = new WebService();
        webService.Url = wsUrl;
        webService.GetLookupsCompleted += 
                new GetLookupsCompletedEventHandler(Lookups_GetLookupsCompleted);
        webService.GetLookupsAsync();
    }

    public static Lookups GetLookups(string wsUrl)
    {
        // If we already have the lookups, just return them
        if (lookups != null) return lookups;

        if (gettingLookupsAsynch)
        {
            // This is the problem code
            [b]
            while (lookups == null)
            {
                System.Threading.Thread.Sleep(1);
            }
            [/b]
        }
        else
        {
            // Not getting async, get them and wait
            WebService webService = new WebService();
            webService.Url = wsUrl;
            lookups = webService.GetLookups();
        }

        return lookups;
    }

    static void Lookups_GetLookupsCompleted(object sender, WebService.GetLookupsCompletedEventArgs e)
    {
        lookups = e.Result;
    }
}

From above, Form1 would call StartLookupsAsync(). Later, Form2 calls GetLookups. Since the asynch call was made, gettingLookupsAsync is true and GetLookups enters the while loop. The while loop appears to block forever. This is my failed attempt at getting it to work.

If the event handler fires before GetLookups is called (which is most of the time), then everything works fine.

I need help! (please!)

-ner
 
The whole point of using asynchronous methods is so that you don't block. And a good way to tell if you’re doing multithreading programming wrong is if your code contains any calls to Sleep().

I do not quite understand what your problem is. Why can’t you just use the event?
 
The way I use the Lookups class is that any form may call GetLookups and that form expects to get a Lookups object returned.

The start of my application kicks off the downloading of the Lookups object by the async call.

I need each form to block/wait for the lookups to finish coming down. The lookups MUST be there for other code on the form to run.

So, the event doesn't help me if a form is loading and needs the lookups. Meaning, how do I make the form "wait" until the event fires?

-ner
 
The problem remains quite simple - yet escapes me for 2.0. It was quite easy in 1.x but MS removed the ability to wait on an asynchronous webmethod.

I can't use your suggestion of the Lookups class loading a form as they'd have no way of knowing what form to load.

The pattern seems VERY common yet I can't find any clues while googling. It seems that this pattern would come up often - start downloading something asynchronously and at some point, when your app needs that download to finish, it must wait until that download is done.

I may have to put in a call to MS if I can't find an answer soon. It seems a bit too common a problem to have to resort to implementing my own threading/asynchronous logic as MS provided it in 1.x and then changed it in 2.0. I'm betting there's something out there to do this, I just haven't found it yet.

And I agree - unresponsive UIs suck. Alas, I can't currently reduce the amount of lookups being returned to the UI. The old code was worse because it wouldn't even begin to load lookups until the form was actually being loaded. The user gets a nice "please wait..." dialog but it took much longer to load as the code returning the lookups was a pure synchronous webmethod call. At least now, they get a headstart downloading.

-ner
 
Although I like the new behaviour I can't understand why they removed the BeginXXX / EndXXX methods.

If you have the .net 1 sdk (or access to the .net 1 wsdl.exe) use that to generate the class and add it to your project. Bit of a hack but it works...

Alternatively you could declare your own delegate that matches the web method's signature and call the delegate asynchronously.
 
Last edited:
Nerseus, I must be missing something or misunderstanding something here, because it is not like you to be so confused about something like this, and I consider myself a multi threading novice.
Nerseus said:
It seems that this pattern would come up often - start downloading something asynchronously and at some point, when your app needs that download to finish, it must wait until that download is done.
The reason for the change in pattern is because the new pattern discourages the blocking of the UI. Instead of waiting by blocking a thread, you simply wait for a handler to be invoked, and meanwhile the thread is free to process messages.

To illustrate how the logic would map out with the two different patterns:
Code:
[Color=Violet]// Begin/End pattern[/Color]
[Color=Blue]void [/Color]DoThings() {
    [Color=Red]// Logic Part A[/Color]
    Object.BeginAsync();
    [Color=Red]// Logic Part B[/Color]
    Object.EndAsync();
    [Color=Red]// Logic Part C[/Color]
}
 
[Color=Violet]// Being/Event pattern[/Color]
[Color=Blue]void [/Color]DoThings() {
    [Color=Red]// Logic Part A[/Color]
    Object.BeginAsync();
    [Color=Red]// Logic Part B[/Color]
} 

[Color=Green]// Handles hypothetical Lookups.LookupFinished event[/Color]
[Color=Blue]void [/Color]FinishThings() {     
    [Color=Red]// Logic Part C[/Color]
}
[Color=Green]// A little more thread-safe logic is
// needed, but the concept should be
// adequately expressed.
[/Color]
Some of the logic must be moved, but there is still certainly a place for it. The only behavioral difference here is that the UI thread is not blocked while we are waiting for the async operation to complete.

The complication in your case is that the UI does not directly handle the async event. Perhaps the Lookup class should be a single-use (non-static) object for which the interested form can subscribe to an event which will be raised when the lookup is complete, enabling you to use the pattern illustrated above. In the time between Logic Part B and Logic Part C you can display a "Working" or similar message to the user. This should work, regardless of whether Logic Part A/B/C is initialization logic or otherwise. It shouldn't hurt you to move some code from the constructor elsewhere. It will probably be more work, but that's life, I suppose. I can see some potential issues, such as if you wanted to perform all this logic before the form is displayed, but it is certainly still do-able. You seem to be reluctant to split up the logic like this, and I don't quite understand why. The fact that you are using static code to perform your lookup isn't helping either. Static code and multi threading don't really seem to mix well with this sort of thing.
 
I suppose my question can be broken into two:
1. Am I doing things the right
2. How do I do what I want

I think most of the answers are for #1, while I need #2 :)

marble's example is very close to what I want but moving logic into an event handler just doesn't make sense. Maybe that's the way it has to be done, but it's certainly not the way it should be done.

Here's a better example:
frmCustomer's constructor (assume all this logic is on the form somewhere):
1 Start getting lookups data from DB
2 Start getting customer data from DB
3 Start getting user "default" values from DB
4 Do some basic initialization that doesn't rely on any data above
5 Start building a business object that requires all 3 of above
6 Bind controls

From that pseudo-code, there are three asynchronous calls. This code clearly needs to block/wait at the time it reaches step 5. While I'm sure you can come up with a way for the three event handlers to trigger some call to a function, which in turn checks that all three have finished, isn't it easier - and just as performant - to block/wait at step 5? It makes the code more readable as well. The code should mimic my intentions so that I can "see" what it's doing.

I can see arguments for doing the binding in the event handler of getting the customer data - you could do that there. Same for the lookups - in their event handler, bind the drop-downs. But if you need multiple asynchronous calls to be finished before moving on, or if you just don't like the disconnected nature of the events and completion because it's not "nice", then why not provide an easier way to wait? I don't think anyone can argue that blocking is "bad" - we all block our UIs to wait for Customer data to load. We all block our UIs to wait for lookups (well, most of us do). We have to get data before we show the form, that's a simple fact.

So, assuming the original question #1 is answered with "Yes, you are doing things right" maybe the answer to #2 is simply "I don't know." At least, that's my answer to #2.

I'm thankful for those of you suggesting alternate routes to handle things asynchronously - it makes me triple check that I'm not doing something crazy. I honestly think I'm doing things the "best" way - especially when it comes to performance.

I will investigate the BackgroundWorker class. Maybe I'll have to "code it by hand" after all, although this seems like such a common problem to solve. It seems odd that MS had support for this pattern (start the webmethod asynchronously and later on, wait for it to complete) then removed it.

-ner
 
You say the code needs to block, if you're on a UI thread then that's a no-no. It's as simple as that. You can't have any functions in your UI thread that wait for anything. Do events really make for ugly code? Calling BeginX methods, doing stuff, then calling EndX methods kills the whole idea (especially on single core systems) of concurrency.
 
With IngisKahn's last statement, I couldn't disagree more. You block/wait for all sorts of things before showing a windows or web UI. If you've ever returned data from a database then your UI is blocking. Sure, you'll have a timeout on the connection - that's perfect. Hopefully you never get near that timeout value, but you're still blocking at some point.

Take the common scenario of calling two stored procedures to get two sets of data. If you agree that you have to have this data before showing the UI then you'll be blocking. Standard code might do this:
Form loads (OnLoad or Load event):
Call stored proc to get lookups
Call stored proc to get customer data
Do some initialization that doesn't require data above
Bind controls

In this scenario, I'm blocking four times - twice for data and twice for standard WinForm code.

Given that scenario, my implementation wants to start getting data asynchronously while the rest of the UI does what it can. At some point before the UI loads, that data has to be there.

I guess my scenario is not as common as I'd hoped or no one else is optimizing the loading of their forms...?

-ner
 
You clearly disagree with IngisKahn, but I agree with him. Given your standard scenario:
Form loads (OnLoad or Load event):
Call stored proc to get lookups
Call stored proc to get customer data
Do some initialization that doesn't require data above
Bind controls

As written you are doing plenty of blocking, but there is no need for it. There is no need for you to call the stored procs on the UI thread. If there is any kind of snag, all windows in the application will stop responding (at least, momentarily). No, its not the end of the world but it isn't necessary and it is frustrating. When a program does this to me it strikes me as a broken UI. Why are you so bent on writing code like that above instead of something like:


Form loads (preferably constructor):
Begin Async Calls
Do some initialization that doesn't require data

Async Completes:

Bind controls
Display Form

Think about it...
This code clearly needs to block/wait at the time it reaches step 5. While I'm sure you can come up with a way for the three event handlers to trigger some call to a function, which in turn checks that all three have finished, isn't it easier - and just as performant - to block/wait at step 5?
Don't think of it as block/wait. Think of it as blocking, or waiting. The UI thread can wait but handle messages, or it can block and appear frozen. And what do you mean by just as performant? Will the Form be displayed just as fast? Of course, but in the mean time you have a choice between having the possibility of a "[Not Responding]" window and not having the possibility. You say the code seems disconnected. Well, that's the difference between procedural programming and event-driven programming. The code is organized based on what it is reacting to.

Given that scenario, my implementation wants to start getting data asynchronously while the rest of the UI does what it can. At some point before the UI loads, that data has to be there.
Sorry to sound like a broken record, but this does not require the amount of blocking you seem to want to do. Clearly, your mind is already made up, so you are left with two options that I can think of. The first would be to use PD's suggestion: wrap 1.x version code in a DLL. The second would be to re-invent the discarded wheel and roll your own Begin/End async methods for re-use later on. I know you must be getting frustrated at this point, but I think that this is the best we can do to answer question #2.
 
In an ideal world, a UI could display before having all the data. In an ideal world, the data would be downloaded almost instantly and the user would never wait. In my world, I have to get at data. If a user clicks a menu item to open a form display customer data, they're not interested in doing anything else - they want their customer form to open. I could definitely let the user "not block" but then what? They still have a "Please wait..." message in front of them and they open something else? Seems kinda odd. If I didn't show them "Please wait..." then they'll have no idea that I started opening a form. I'm older, but I can't imagine another way to show a form without waiting for the data it needs - in another thread or not, that data has to be there.

Using a real world product, like MS Word, they do the same thing. If I open a large word doc (say 56 meg), I don't get a responsive UI while the file loads. I get MS Word starting to open and it "hangs" until it opens. I suppose they could have let me click around the menus while I'm waiting, but that seems kinda pointless.

So to answer #2 I did quite a bit more digging/investigating. The only thing working so far is to use Application.DoEvents(). While I'm not 100% sure, I'm guessing my situation is complicated by my use of static variables and methods. I won't go into details as my solution seems like PDs answer - go back to the way it was working in 1.x and hope that one day they put back that functionality. For now, I don't mind "recreating the wheel" for what I need, though I wish I didn't have to.

Thanks for the help and insight, it's good to bounce ideas around to double check my thoughts.

-ner
 
User clicks menu.
New form appears.
Controls are populated/enabled as data becomes available. (Setting the form caption to "X - Loading" in the interim is an option.)

The only difference between this and your method is that the user is not faced with a seemingly broken application while it is loading.

BTW, MS themselves said that they have had bad UI thread practices in the past.
 
Well, we can only beat a dead horse for so long. Just let me ask this one question, Nerseus (or PD, or whom ever). Would wrapping the 1.x library and using it through 2.0 require that the end user has DotNet 1.x installed? Of course, I don't expect it to be an issue, but from a purely academic point of view it would be a shame to depend on two versions of the framework.
 
You should be able to use the source file generated by the .Net 1.1 tool in a .Net 2 project without any modification - after all if you upgrade a project to .Net 2 it doesn't alter how you call a webservice.

Regardless of the blocking / unblocking issue for a forms UI I do personally feel the loss of the .Net 1 methods is a shame - the event model is definately easier (and cleaner in most scenarios) but isn't that useful if you do not have a windows UI (a windows service or console app for example), need to co-ordinate multiple requests (wait handles are very useful here) and if you are using async web pages (this last one is interesting because VS does actually generate both the BeginXXX and EndXXX methods as well as the XXXAsync / Event pair when you add a web reference to a web application.
 
In the end it was easiest to just cut and past the BeginX and EndX methods from the 1.x generated web proxy code into a partial class. That way, I can use the new webmethods and the old ones (when needed). I only need the BeginX/EndX methods in a few cases (mostly for returning lookups), so the partial class works great. MS can reorganize my main webmethod files as needed without throwing away my BeginX/EndX methods.

In a perfect world, the 100+ forms I already have written would be a lot cleaner, load faster, and have less business logic tied into the UI. Reality says this product is too big to do all at once and it will be years before it's in a state to revamp the UI's loading of data.

There are so many things I'd like to change, to make the code nicer and give the user a better experience - but they're generally not paying for it and I don't have the time to do it on the side anymore.

-ner
 
Back
Top