views:

107

answers:

2

I have a web service with two methods:

RetrieveFirstLevelOptions() and RetrieveSecondLevelOptions(int levelOneOption).

The GUI contains two comboBoxes: FirstLevelOptionsCombo and SecondLevelOptionsCombo.

I am having trouble with creating a control flow for the initialization stage when I have to make a request to RetrieveFirstLevelOptions() and then, once the results are in, call RetrieveSecondLevelOptions(default levelOneOption = 0).

The problem is that since everything happens asynchronously I don't know what the best approach is to allow this behaviour take place once and only once, at the beginning.

An option I would love to have is to attach a second event handler to the RetieveFirstLevelOptionsCompleted event and have it remove itself after running only once. But it looks like such a behaviour is impossible to get.

Another option would be to have a boolean flag to indicate whether in Initialization phase and if it is, then the handler for RetrieveFirstLevelOptionsCompleted would execute some additional logic. However this idea makes it look like all event handlers would have to check for state information and do different things depending on the current state. This seems to be bad design because the control flow seems to be descentralized.

I want to have a centralized control flow mechanism that will make the decisions in one spot. But how can this be done when everything is executed asynchronously?

+1  A: 

The pattern that I usually use in a situation like this is to create a wrapper around the Async web service proxy method that accepts a callback method. The callback method then gets passed to the RetrieveFirstLevelOptionsAsync() method like so:

public void RetrieveFirstLevelOptions(Action callback)
{
    client.RetrieveFirstLevelOptionsAsync(callback);
}

void client_RetrieveFirstLevelOptionsCompleted(object sender, AsyncCompletedEventArgs e)
{
    var callback = e.UserState as Action;
    if (callback != null)
    {
        callback();
    }
}

So when you call RetrieveFirstLevelOptions(), you just pass the callback that you want to run only once, and you don't ever have to worry about it getting called multiple times. Presumably you'd put your call to RetrieveSecondLevelOptions() within that callback.

Ken Smith
If you don't mind me asking, how is this mechanism guaranteeing that callback will be called exactly once during the application lifetime, as part of the initialization phase?
Rire1979
By itself, technically it doesn't. My assumption is that this is only a concern because there might be multiple times that the first call gets made (RetrieveFirstLevelOptions), but you only want to make the second call during initialization. (Otherwise, why worry?) So in that scenario, this wrapper does allow you to call make the first call during initialization, and be confident that your second call (RetrieveSecondLevelOptions) isn't going to get made every other time you need to call RetrieveFirstLevelOptions.
Ken Smith
+1  A: 

"An option I would love to have is to attach a second event handler to the RetieveFirstLevelOptionsCompleted event and have it remove itself after running only once. But it looks like such a behaviour is impossible to get."

Is there some reason this isn't working:

class Example
{
    SomeWebService myService;

    Example()
    {
        // do stuff
        myService.RetrieveFirstLevelOptionsCompleted += MyHandlerMethod;
    }

    void MyHandlerMethod(object sender, RetrieveFirstLevelOptionsCompletedEventArgs e)
    {
        // do stuff
        myService.RetrieveFirstLevelOptionsCompleted -= MyHandlerMethod;
        // potentially attach next event handler for all subsequent calls
        myService.RetrieveFirstLevelOptionsCompleted += MyHandlerMethod2;
    }
}
jeffora
I used to use that approach, but it always seemed like a hack to me. There are two basic problems with it: (1) It makes it hard to keep track of which handler is currently attached, since it spreads out your logic into a variety of places; and (2) if you happen to make a second call to myService.RetrieveFirstLevelOptions before the first one completes, you're going to end up with some unexpected behaviors that will be very difficult to debug. A wrapper that accepts a specific callback is easier to understand and harder to get wrong.
Ken Smith
I agree to a certain extent, however I think implementation depends on the specifics of what you are trying to achieve. This answer was more directed at his quote saying that he wasn't able to remove an event handler. I've not come across too many situations where this is a better method than even using a simple if (initialised) check
jeffora