Faking Synch WCF calls in WP7

So you can’t do synchronous WCF calls in Windows Phone 7. I have a use case where I do a WCF call to get all of the data to set up a game board of the phone game I am writing. I don’t want the application to progress until the board is set up, so I need it as a blocking call. In addition, the WCF call is 5-6 layers deep in my class hierarchy.  My first idea was just to use a non-thread-safe variable from the Main UI thread and set the flag when the asynch call/thread returned:

        private List<ScenarioTile> GetScenarioTilesFromWebService(int scenarioId)
        {
            CallbackComplete = false;
            PanzerProxy.PanzerClient panzerClient = new PanzerProxy.PanzerClient();
            panzerClient.GetScenarioTilesCompleted += new EventHandler<PanzerProxy.GetScenarioTilesCompletedEventArgs>(panzerClient_GetScenarioTilesCompleted);
            panzerClient.GetScenarioTilesAsync(scenarioId);
            while (CallbackComplete == false)
            {
                Thread.Sleep(1000);
            }
            return ScenarioTiles;
        }

        void panzerClient_GetScenarioTilesCompleted(object sender, PanzerProxy.GetScenarioTilesCompletedEventArgs e)
        {
            ScenarioTiles = new List<ScenarioTile>();
            List<PanzerProxy.ScenarioTile> proxyTiles = e.Result as List<PanzerProxy.ScenarioTile>;
            foreach (PanzerProxy.ScenarioTile proxyScenarioTile in proxyTiles)
            {
                ScenarioTiles.Add(ConvertProxyScenarioTileToScenarioTile(proxyScenarioTile));
            }
            CallbackComplete = true;
        }

The problem is that, well, it doesn’t work. The app just hangs. I then thought of using a ManualResetEvent or AutoResetEvent class to trigger like this:

        ManualResetEvent manualResetEvent = new ManualResetEvent(false);

        private List<ScenarioTile> GetScenarioTilesFromWebService(int scenarioId)
        {
            PanzerProxy.PanzerClient panzerClient = new PanzerProxy.PanzerClient();
            panzerClient.GetScenarioTilesCompleted += new EventHandler<PanzerProxy.GetScenarioTilesCompletedEventArgs>(panzerClient_GetScenarioTilesCompleted);
            panzerClient.GetScenarioTilesAsync(scenarioId);
            manualResetEvent.WaitOne();
            return ScenarioTiles;
        }

        void panzerClient_GetScenarioTilesCompleted(object sender, PanzerProxy.GetScenarioTilesCompletedEventArgs e)
        {
            ScenarioTiles = new List<ScenarioTile>();
            List<PanzerProxy.ScenarioTile> proxyTiles = e.Result as List<PanzerProxy.ScenarioTile>;
            foreach (PanzerProxy.ScenarioTile proxyScenarioTile in proxyTiles)
            {
                ScenarioTiles.Add(ConvertProxyScenarioTileToScenarioTile(proxyScenarioTile));
            }
            manualResetEvent.Set();
        }

Still no luck.  I then ran across this thread – basically you can’t do anything to block the Main UI thread.  Chong’s solution was to launch a 3rd thread from the secondary thread.  That seemed to me as overly complex and smelled of brittle code.  I then took a step back and realized I just had to architect my solution to get the same effect.  Basically, once the user hits the LOAD button, the MAINUI thread is done.  However, the page also subscribes to an event that the business logic raises.  Once the event fires, then the app progresses to the next page.

So instead of this:

private void SetupScenario(int scenarioId)
{
    Game.CurrentScenarioId = scenarioId;
    Game.BoardFactory.PopulateBoard(scenarioId);
    Game.CurrentBoard = Game.BoardFactory.Board;
    Game.Turns = Game.TurnFactory.GetTurnsForAScenario(scenarioId);
    this.NavigationService.Navigate(new Uri(@"/Briefing.xaml", UriKind.Relative));
}

I have this:

private void SetupScenario(int scenarioId)
{
    Game.CurrentScenarioId = scenarioId;
    Game.BoardFactory.BoardLoaded += new EventHandler<EventArgs>(BoardFactory_BoardLoaded);
    Game.BoardFactory.PopulateBoard(scenarioId);
    Game.CurrentBoard = Game.BoardFactory.Board;
    Game.Turns = Game.TurnFactory.GetTurnsForAScenario(scenarioId);

}

void BoardFactory_BoardLoaded(object sender, EventArgs e)
{
    this.NavigationService.Navigate(new Uri(@"/Briefing.xaml", UriKind.Relative));
}

The event is defined as simple as it comes:

public event EventHandler<EventArgs> BoardLoaded;

and

void TileFactory_TilesLoaded(object sender, EventArgs e)
{
    Tiles = Game.TileFactory.Tiles;
    Hexes = Game.HexFactory.GetHexes(Tiles);
    foreach (Hex hex in Hexes)
    {
        Board.MainCanvas.Children.Add(hex);
    }
    SetBoardDimensions(Board, Tiles);
    BoardLoaded(null, null);
}

Basically I just bubbled up the WCF asych event up from the lowest level to the UI page.

Advertisements

2 Responses to Faking Synch WCF calls in WP7

  1. Henry says:

    Great post- the difference between this scenario and my contact search one you linked to, though, is that I wanted my method to return an object (whereas here some stuff just gets executed) – I might have been able to pass in some other object, maybe some sort of composite request type, to get around having to spawn a third thread, but totally agree with you that it’s not something anyone would really want to be doing – if you’ve got an alternative, would love to hear it 🙂

    Are you also by any chance making a panzer general app for wp7? 😀

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: