This article demonstrates how to consume a foreign webservice with the goals of demonstrating synchronous and asynchronous data extraction against it. Secondly it will show how to do this on another thread with proper winform data invokation to the GUI thread.

The developer who is learning how to consume a webservice and populate data has two hurdles to overcome. The first is working with the webservice and the second is properly populating the data back to the win service. Here are the goals

  1. Asynchronous webservice consumption.
  2. Synchronous webservice consumption.
  3. Threaded webservice consumption with the handling of cancel and background worker thread.
  4. Invoking data back to the winform GUI.

Background: This article will gloss over these topics, so look to learn elsewhere

  1. Basic winform project creation and control handling.
  2. Understanding of the basics on how to connect to a webservice.

Setup

This phase is the initial phase where we create the project foot print.

  1. Create a standard winform project.
  2. Add web reference to http://www.weather.gov/forecasts/xml/DWMLgen/wsdl/ndfdXML.wsdl and change the web reference name to GovWeather. (Note for this web service consumption to work you will need to see my blog Consuming NOAA and Avoiding Protocol Violation in .Net).
  3. Place three buttons on the form labeled Async, Sync and Thread.
  4. Place a RichText Dialog on the screen and name it richTextBoxXmlDisplay. This will display our acquired data or exceptions.

Synchronous Call and Data Display

This one is the easiest to code but it does have a problem. We will simply call the web service directly and then push the data to the RichTextBox. Add this code to the button press for the synchronous event.

try
{
    richTextBoxXmlDisplay.Text = GetDataSynchronously();
}
catch (System.Exception ex)
{
    richTextBoxXmlDisplay.Text = ex.Message;
}

Here is the actual function which gets the data synchronously from the web service, we will actually use it later.

private string GetDataSynchronously()
{

 

    WebServiceConsumer.GovWeather.ndfdXML weather = new WebServiceConsumer.GovWeather.ndfdXML();

 

    return weather.NDFDgenByDay(39.5946m,
                               -105.014m,
                               DateTime.Now,
                               "1",
                               WebServiceConsumer.GovWeather.formatType.Item24hourly);

 

}

But this has a major problem, if the webservice takes a long time to process our user experience will become locked up. This is fine for console applications or background threads but not for true GUI processing.

Asynchronous Call and Data Display

Asynchronous data extraction will play better with GUI thread. First we start by creating a member variable to hold the web service object.


 

WebServiceConsumer.GovWeather.ndfdXML _weather;

We will initialize the _weather object in the button event for async wiring in the asynchronous operations as so:

try
{
    _weather = new WebServiceConsumer.GovWeather.ndfdXML();

 


    _weather.NDFDgenByDayAsync(39.5946m,
                               -105.014m,
                               DateTime.Now,
                               "1",
                               WebServiceConsumer.GovWeather.formatType.Item24hourly);

 

    // Intellisense will be kind and do all the footwork
    // to create the stub for the target.
    _weather.NDFDgenByDayCompleted
        += new WebServiceConsumer.GovWeather.NDFDgenByDayCompletedEventHandler(_weather_NDFDgenByDayCompleted);

 

    richTextBoxXmlDisplay.Text = "Web Service Called...";
}
catch (System.Exception ex)
{
    richTextBoxXmlDisplay.Text = ex.Message;
}

This is the same as the Synchronous except we don’t expect an immediate result. Also we wire in an event for the completion status. By simply typing in _weather.NFDgenByDatCompleted += we can have Visual Studio wire in the actual event call and stub completion. In the stub, remove the throw line and add this information:

if (e.Cancelled == false)
{
    richTextBoxXmlDisplay.Text += "Finished" + Environment.NewLine;

 

    richTextBoxXmlDisplay.Text += e.Result;
}

When the web service is complete, the event is fired, and because it is a winform event, we are guaranteed that the work will be on the GUI thread. That assurance allows us to write directly to the RichTextBox.

Threaded Call and Data Display

The threaded call is a requires a little more preparation and a little more foresight than the other methods. We are going to start with the things we have to declare as member variables

#region Variables
    // The thread to be used to do the work.
    private Thread _weatherThread;

 

    // The result or error message to be written.
    private string _result;

 

    // The delegate used to invoke back to the main GUI thread.
    public delegate void DataAcquired(object sender);

 

    // The invoke event.
    public event DataAcquired OnDataAcquiredEvent;
#endregion

The thread object is obvious, it will hold the worker thread. The result of the operations will be placed on _result. The following two items are required because the worker thread cannot do any GUI writing to the string. Because of that we are going to invoke an event to the main GUI thread to write out the _result.

The first thing we can do is seed the event to write out the _result. We will subscribe to the event OnDataAcquiredEvent in the forms constructor.


 

OnDataAcquiredEvent += new DataAcquired(ThreadReportsDataAquiredEvent);

Then we create the method ThreadReportsDataAquiredEvent for the member variable OnDataAcquiredEvent

private void ThreadReportsDataAquiredEvent(object sender)
{
    richTextBoxXmlDisplay.Text += _result;
}

Pretty basic, we are ignoring the sender object in the example, I am leaving it in if you need to do something special that should be passed in.

The next step is to create the thread _weatherThread to do the actuall work. Wire up the Thread button event with this code:

_weatherThread = new Thread(GetWeather);
// If the user closes the application, the thread will not stay alive
// By placing this as a background thread.
_weatherThread.IsBackground = true;
_weatherThread.Start();

The above code is smart enough to set the thread to be a background status. That does not affect priority or processing. It just ensures that if the application is closing, then this thread won’t continue to work autonomously and hold up the closing of the program.

We now can create the target operation of the thread GetWeather such as

private void GetWeather()
{
    bool InvokeToGui = true;

 

    try
    {
        _result = GetDataSynchronously();
    }

 

    catch (ThreadAbortException)
    {
        // We have handled the exception and will simply return without data.
        // Otherwise the abort will be rethrown out of this block. 
        Thread.ResetAbort();
        InvokeToGui = false; // No sense displaying anything.
    }
    catch (System.Exception ex)
    {
        _result = ex.Message;
    }
    finally
    {
        // We invoke back to the main thread to
        // update the result. We pass in a null, you 
        // may want to change that to pass actual information
        // if running multiple threads.
        if (InvokeToGui)
            this.Invoke(this.OnDataAcquiredEvent, new object[] { null });
    }

 

}

This function does these things

  1. Simply calls the GetDataSynchronously because we are a thread and don’t need to call it asynchronously. Note if you have timeout or cancel issues you may want to rig up an asynchronous call within!
  2. This handles the ThreadAbortException. We use it in this test example, but it is good to handle it if in the future there is need or if it comes from another source.
  3. Simply writes the result to _result. We don’t need to lock result since it is only used by us between this thread and the GUI thread.
  4. Lastly we will invoke to the main thread the event that results are available!

Summary

That wraps it up, you now have three different ways to consume a webservice and safely update the GUI winforms as well.

Share