Microsoft & .NET.NETAsynchronous Web Services Invocation in .NET Framework 2.0

Asynchronous Web Services Invocation in .NET Framework 2.0

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Synchronous communication between .NET applications and Web services makes the user wait while each Web service processes requests and returns results. This can have a severe impact on the performance of the .NET application. Typically, a distributed .NET application requires information from multiple Web services. If the application performs the entire process of invoking the Web services synchronously, a client must wait not only until every Web service provider is contacted, but also through any connection or service delays.

Asynchronous Web services invocation solves this performance issue and enhances the end user experience by increasing server efficiency. With the introduction of .NET Framework 2.0, Microsoft has greatly enhanced the support for asynchronous Web services invocation by introducing a new event-based programming model. This article examines this new feature and demonstrates how to take advantage of it to create feature-rich and effective .NET applications. It also shows how to perform data binding directly with the results the Web service returns.

Event-Based Asynchronous Programming

Previous versions of the .NET Framework used the BeginInvoke/EndInvoke methods to invoke a Web service asynchronously. Version 2.0 adds a new way to asynchronously invoke Web services: an event-based asynchronous programming model. It enables this new event programming model through the creation of properties and methods on the client proxy class.

To follow the demonstration for implementing this model, you must first create a simple Web service that then can be invoked asynchronously from the client application. The following section walks you through the process.

Create a Simple Web Service

Open Visual Studio 2005 and select New->Web Site from the File menu. In the New Web Site dialog box, select ASP.NET Web service from the list of project templates, and specify the name of the Web service as AsyncWebServices.

Once you have created the Web site, add a new Web service file named HelloService.asmx to the project. If you open the file and view the code, you will find a method named HelloWorld already placed in the code-behind file of the Web service. For the purposes of this example, just utilize this default sample method. Here is the code from the code-behind file of the HelloService:

using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1,
                   EmitConformanceClaims = true)]
public class HelloService : System.Web.Services.WebService 
{
    [WebMethod]
    public string HelloWorld()
    {
        return "Hello World";
    }
}

Note: When you create a Web service through Visual Studio 2005, the code-behind file for the Web service is automatically placed in the App_Code directory, which is a new directory in ASP.NET 2.0 that acts as a container for all the reusable classes in an ASP.NET Web application.

Now that you’ve created the Web service, if you right click on it and select View in Browser, you will see the screen shown in Figure 1.

Figure 1. View Newly Created Web Service in Browser

If you click on the HelloWorld method, you will see the screen shown in Figure 2.

Figure 2. View HelloWorld Method in Browser

Clicking on the Invoke button in the Figure 2 results in the screen shown in Figure 3.

Figure 3. Invocation of HelloWorld Method

Figure 3 shows the output produced by the HelloWorld method. Now that you have implemented and tested the Web service, you can move on to the client application that will consume the Web service.

Consume the Web Service Asynchronously from a Windows Forms Application

In this section, you create a Windows Forms application to consume the Web service. Create a new Visual C# Windows Forms application named AsyncWebServicesClient. Next, add a Web reference to the CategoriesService by selecting Project->Add Web Reference from the menu. In the Add Web Reference dialog box, enter the location of the Web service and hit GO. Then, specify the name of the Web reference as HelloWorldServiceProxy, as shown in Figure 4.

Figure 4. Specify the Name of the Web Reference as HelloWorldServiceProxy

Now you are ready to consume the Web service. To start, import the Web service proxy namespace at the top of the Form1:

using AsyncWebServicesClient.HelloWorldServiceProxy;

To the form, add a command button named btnInvokeHelloWorld and modify the click event of the command button to look as follows:

private void btnInvokeHelloWorld_Click(object sender, EventArgs e)
{
    HelloService service = new HelloService();
    //Hookup async event handler
    service.HelloWorldCompleted += new 
        HelloWorldCompletedEventHandler(this.HelloWorldCompleted);
    service.HelloWorldAsync();
}

The above code shows the event handler HelloWorldCompleted (that is available through the proxy of the Web service) being hooked up to a local method named HelloWorldCompleted. The method named HelloWorldAsync provides a way to asynchronously invoke the HelloWorld method.

The new <MethodName>Async provides a seamless way to asynchronously consume the Web service from a client application. For example, if you have a Web service method named GetCategories and you want to leverage the asynchronous invocation framework, you need to do two things:

  • Create an event handler for the GetCategoriesCompleted method and hook it up to a local method that can process the results returned by the Web service.
  • Invoke the Web service method by calling the GetCategoriesAsync() method through the Web service proxy.

The local HelloWorldCompleted method is defined as follows:

void HelloWorldCompleted(object sender,
                         HelloWorldCompletedEventArgs args)
{
    //Display the return value
    MessageBox.Show(args.Result);
}

After the Web service has finished its execution and returned the results, the .NET Framework will automatically invoke the above HelloWorldCompleted method. As you can see, this method takes two arguments: an object of type “object” and a “HelloWorldCompletedEventArgs” object. You can capture the results returned by the Web service by using the Result property of the HelloWorldCompletedEventArgs object (as shown in the above code). In this example, the output the Web service returns is displayed in a message box.

Implement Data Binding with a Web Service

Apart from the new Web service asynchronous invocation framework, .NET Framework 2.0 also introduces a new feature through which you can perform data binding, with the results returned from the Web service in a seamless manner. This section demonstrates this feature with a practical example. It begins by creating the Web service.

Implementation of the Web service

To start, add a new Web service to the AsyncWebServices project by right-clicking on the project and selecting Add New Item from the context menu. In the Add New Item dialog box, select Web service from the list of templates and name the Web service CategoriesService.asmx. Modify the code-behind of the CategoriesService.asmx file to look as follows:

using System;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1,
                   EmitConformanceClaims = true)]
public class CategoriesService : System.Web.Services.WebService
{
[WebMethod]
    public DataSet GetCategories()
    {
        try
        {
            using (SqlConnection conn = new SqlConnection())
            {
                string connectionString =
                    System.Configuration.ConfigurationManager.
                ConnectionStrings["northwindConnectionString"].
                ConnectionString;
                conn.ConnectionString = connectionString;
                SqlCommand command = new SqlCommand
                    ("Select * from Categories", conn);
                command.CommandType = CommandType.Text;
                SqlDataAdapter adapter = new
                    SqlDataAdapter(command);
                DataSet categories = new DataSet("Categories");
                adapter.Fill(categories);
                return categories;
            }
        }
        catch (Exception ex)
        {
            EventLog.WriteEntry("Application", ex.Message);
            throw ex;
        }
    }

}

Line-by-Line Code Breakdown

The above code first encloses the SqlConnection object within the scope of a using block. Then, it retrieves the connection string from the ConnectionStrings section of the web.config file:

string connectionString = System.Configuration.ConfigurationManager.
    ConnectionStrings["northwindConnectionString"].
    ConnectionString;

The code stores the connection string directly under the root <configuration> element in the web.config as follows:

<connectionStrings>
    <add name="northwindConnectionString"
         connectionString="server=localhost;uid=sa;pwd=thiru;
                           database=northwind"/>
</connectionStrings>

Then, it sets the retrieved connection string to the ConnectionString property of the SqlConnection object. After that, the code creates an instance of the SqlCommand object, passing to its constructor the SQL statement to be executed and the SqlConnection object as arguments. Then, it sets the CommandType property of the SqlCommand object to CommandType.Text to indicate that it wants to execute a SQL statement through the SqlCommand object:

SqlCommand command = new SqlCommand("Select * from Categories", conn);
command.CommandType = CommandType.Text;

Next, it creates an instance of the SqlDataAdapter and passes in the SqlCommand object to its constructor:

SqlDataAdapter adapter = new SqlDataAdapter(command);

After that, the code creates a DataSet object and then supplies the DataSet object as an argument to the Fill method of the SqlDataAdapter object:

DataSet categories = new DataSet("Categories");
adapter.Fill(categories);

Finally, it returns the DataSet object back to the caller using the return statement:

return categories;

Now that you have seen the implementation of the Web service, take a look at the client application that will consume the Web service.

Consume the Web Service from the Client

Open the previously created AsyncWebServicesClient project from Visual Studio 2005. Add a Web reference to the CategoriesService by selecting Project->Add Web Reference from the menu. In the Add Web Reference dialog box, enter the location of the Web service and specify the name of the Web reference as CategoriesServiceProxy. Once the Web reference is created, you can import the namespace of the proxy using the following line:

using AsyncWebServicesClient.CategoriesServiceProxy;

Next, open Form1 and drag and drop a DataGrid control onto the Form designer. Add another command button named btnInvokeCategories to the form and modify the code in the Click event to look like the following:

private void btnInvokeCategories_Click(object sender, EventArgs e)
{
    CategoriesService service = new CategoriesService();
    service.GetCategoriesCompleted += new
        GetCategoriesCompletedEventHandler
        (this.GetCategoriesCompleted);
    service.GetCategoriesAsync();
}

The implementation of the GetCategoriesCompleted method is as follows:

void GetCategoriesCompleted(object sender,
                            GetCategoriesCompletedEventArgs args)
{
    //Bind returned results to the UI data grid
    dataGrid1.DataSource = args.Result;
}

As you can see, the DataSet returned from the Web service is directly bound to a DataGrid control and displayed on the form. If you run the form and click on the Invoke Categories Service button, you will see the output shown in Figure 5.

Figure 5. Run the Form and Click on the Invoke Categories Service Button

As Figure 5 shows, the categories information that the Web service returns is directly data bound to the DataGrid control.

Increase Developer Productivity, Enhance User Experience

The new asynchronous invocation framework that .NET Framework 2.0 introduced is an extremely useful feature that can go a long way toward increasing developer productivity. You can leverage the framework to increase the responsiveness of a .NET application, which greatly enhances the user experience as well.

This tutorial provided a thorough discussion of the asynchronous Web service invocation features of .NET Framework 2.0 and provided examples. It also showed how to perform data binding with the results returned from a Web service. Although the application you created was simple in functionality, it should provide a solid foundation for understanding how to easily implement the asynchronous Web service invocation feature in your .NET applications.

Download the Code

To download the accompanying source code for the demo, click here.

About the Author

Thiru Thangarathinam has six years of experience in architecting, designing, developing, and implementing applications using object-oriented application development methodologies. He also possesses a thorough understanding of the software life cycle (design, development, and testing). He holds several certifications, including MCAD for .NET, MCSD, and MCP. Thiru is an expert with ASP.NET, .NET Framework, Visual C# .NET, Visual Basic .NET, ADO.NET, XML Web services, and .NET Remoting. Thiru also has authored numerous books and articles. Contact him at thiruthangarathinam@yahoo.com.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories