To read the article online, visit http://www.4GuysFromRolla.com/articles/011409-1.aspx

Building Interactive User Interfaces with Microsoft ASP.NET AJAX: Retrieving Server-Side Data Using Web Services

By Scott Mitchell


Introduction


Microsoft's ASP.NET AJAX framework offers two models for developing interactive web applications: client-centric and server-centric. With the server-centric model, developers use the standard ASP.NET controls - the GridView, Buttons, TextBoxes, and so forth - but place them within an UpdatePanel control. The UpdatePanel control automatically converts normal postbacks to partial page postbacks and seamlessly updates the page's display with any modifications made by server-side code. On the other hand, with the client-centric model the developer is responsible for writing the JavaScript that performs any asynchronous requests to the server, as well as the script that updates the page on response.

The server-centric model is easier to use and more familiar to developers who have a solid background with ASP.NET controls, but who are not as comfortable with JavaScript and HTML. However, that ease of use comes at a cost: the server-centric model shuttles a substantial amount of data between the client and server on each partial page postback. In short, the UpdatePanel sends the page's view state to the server on a partial page postback and receives this (perhaps modified) view state back in response, regardless of whether the view state is needed to perform the server-side logic. Moreover, the entire rendered contents of the UpdatePanel are returned on response, even if only a small portion of the content in the UpdatePanel has been modified.

The client-centric model involves a bit more work than the server-centric model and a familiarity with HTML, JavaScript, and the Document Object Model (DOM). But using the client-centric model can greatly reduce the quantity of information exchanged between the client and server during a partial page postback. This article looks at one technique for retrieving server-side data in the client-centric model. Read on to learn more!

A Web Service Primer


Web Services are a mechanism by which two computers can interact over a network. Web Services are typically implemented using the client/server model, where a server exposes a Web Service which is then consumed by a client. For example, a weather website might provider a web service that returns current conditions for a particular city. A client application, be it a desktop application or another website, could call this web service passing along the city of interest. The weather website would respond by returning the current conditions for that city, which could then be displayed by the client application. In a nutshell, Web Services allow for machine-to-machine communication by using standardized transport and encoding protocols.

Most Web Services send data back and forth using SOAP, which is an XML-based protocol for encoding messages. For example, when a client application calls the weather Web Service it would do so by sending a message like the following to the Web Service's endpoint (e.g., the Web Service's URL, like www.weathersite.com/GetWeatherReport.asmx):

<soap:Envelope>
   <soap:Body>
      <GetWeatherReport>
         <CityName>San Diego</CityName>
      </GetWeatherReport>
   </soap:Body>
</soap:Envelope>

The Web Service would respond by sending the current conditions in San Diego, encoded in a SOAP message:

<soap:Envelope>
   <soap:Body>
      <GetWeatherReportResponse>
         <Conditions>Cloudy</Conditions>
         <Temperature>56</Temperature>
      </GetWeatherReportResponse>
   </soap:Body>
</soap:Envelope>

Creating and consuming Web Services in ASP.NET is easy because the .NET Framework handles all of the low-level, nitty-gritty work, such as generating, sending, receiving, and parsing the SOAP messages. To create a Web Service you just need to add a new Web Service file to your website project. This creates a class where you can define what methods are accessible via the Web Service. Likewise, when consuming a Web Service from a desktop or web application, you just point to the Web Service and Visual Studio automatically creates a proxy class, which has the same interface as the Web Service. To call the Web Service from the client application, simply call the corresponding method in the proxy class.

Calling Web Services from an ASP.NET AJAX Application


As noted in the Introduction, when developing ASP.NET AJAX applications using the client-centric model developers are responsible for writing the JavaScript that performs and asynchronous calls to the server and updates the page's display on response. One option for invoking server-side logic is to call a Web Service. But calling a Web Service from client-side script introduces two challenges:
  1. Web Services messages are usually encoded according to the SOAP protocol. However, AJAX applications typically prefer JSON, an alternate encoding standard better suited to JavaScript.
  2. When consuming a Web Service from a client application Visual Studio creates a proxy class on the client that mirrors the interface of the Web Service. However, when calling a Web Service from client-side JavaScript we need a JavaScript-based proxy class instead.
The good news is that Microsoft addressed both of these challenges in a way where the developer has very little to do to overcome them. For the first challenge, Microsoft added attribute classes to the .NET Framework that you can use to decorate your Web Service class to indicate that the Web Service should also support JSON encoded messages. In a nutshell, you can instruct the .NET Framework to allow clients to call your Web Service using JSON messages in addition to the standard SOAP messages by adding just one line of code.

The second challenge is handled by the ScriptManager control. Recall that any page that uses the ASP.NET AJAX framework needs a ScriptManager control on the page. In the examples thus far in this series, we've simply added this control to the page and did nothing with it. However, you can reference any number of Web Services from the ScriptManager, which creates and exposes a JavaScript-based proxy class. As a result, you can invoke the Web Service from client-side script by using this proxy class, which means you don't need to write any code that serializes or deserializes messages, sends the message, and so on.

Creating a Web Service That Can Be Called from an ASP.NET AJAX Application


To illustrate calling a Web Service from an ASP.NET AJAX application, let's create a simple Web Service in our ASP.NET website and then build a page that calls this Web Service from JavaScript and displays the results on the page. (The code demonstrated in this article is available for download at the end of this article.) In particular, let's create a Web Service accepts two integers as input and returns their sum.

Start by adding a new Web Service to your ASP.NET website by right-clicking on the folder where you want the service's endpoint to exist, choosing Add New Item, and then picking the Web Service template. Name this new Web Service NorthwindServices.asmx. The code for the Web Service is located in a separate file named NorthwindServices.asmx.vb or NorthwindServices.asmx.cs and is where we define the methods for our Web Service. Create an Add method by typing in the following code:

Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols

<WebService(Namespace:="http://tempuri.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Public Class NorthwindServices
   Inherits System.Web.Services.WebService

   <WebMethod()> _
   Public Function Add(ByVal x As Integer, ByVal y As Integer) As Integer
      Return x + y
   End Function
End Class

In order for this method to be accessible to an ASP.NET AJAX application we need to mark the service as being a "script service." To accomplish this all we need to do is decorate the Web Service class with the System.Web.Script.Services.ScriptService attribute. Simply add this attribute before the Web Service class definition, like so:

<WebService(Namespace:="http://tempuri.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
<System.Web.Script.Services.ScriptService()> _
Public Class NorthwindServices
   ...
End Class

With that additional attribute the NorthwindServices Web Service can now accept JSON messages in addition to the standard SOAP messages.

Calling the Web Service from an ASP.NET AJAX Application


To call a Web Service from an ASP.NET AJAX application you first need to inform the ScriptManager control that you need a JavaScript-based proxy class for the service. This is accomplished by defining the path to the service in the ScriptManager control's <Services> section:

<asp:ScriptManager ID="ScriptManager1" runat="server">
   <Services>
      <asp:ServiceReference Path="pathToWebService" />
   </Services>
</asp:ScriptManager>

This addition causes the ScriptManager to automatically generate a JavaScript proxy class and make it accessible to the page. To call the Web Service from JavaScript you simply enter the fully qualified type of the Web Service (such as NorthwindServices or, if you have placed the Web Service in a namespace, then Namespace.NorthwindServices), passing in any parameters. The proxy class calls the service asynchronously, meaning that you need to provide a JavaScript function that will be called once the Web Service call completes. It is from this function that you'll update the page's display to incorporate the information returned by the Web Service. (You can also specify a function to call if the Web Service call resulted in an error.)

The demo available for download at the end of this article includes a page that uses the Web Service's Add method. The page contains two TextBox controls where the user can enter the numbers to sum. There's then a button that, when clicked, calls the Web Service from JavaScript and displays the sum in a <span> element named sum. The markup for this user interface follows:

<asp:TextBox runat="server" ID="Num1" Columns="3"></asp:TextBox>
+
<asp:TextBox runat="server" ID="Num2" Columns="3"></asp:TextBox>
=
<span id="sum"></span>
<br />
<input type="button" id="AddNumbers" value="Comute Sum" onclick="ComputeSum()" />

Note that the "Compute Sum" button is implemented as an <input type="button" /> rather than a Button Web control. This is because when the button is clicked we want to run JavaScript code (instead of performing a postback). The button's client-side onclick event handler references a JavaScript function named ComputeSum. The ComputeSum function calls the Web Service's Add method, passing in the values entered by the user in the Num1 and Num2 TextBox controls.

function ComputeSum() {
   var n1 = document.getElementById('<%=Num1.ClientID %>').value;
   var n2 = document.getElementById('<%=Num2.ClientID %>').value;

   NorthwindServices.Add(n1, n2, OnComputeSumSuccess, OnComputeSumError);
}

The JavaScript function document.getElementById(id) function is used to grab the associated HTML element in the document. Because the controls in question - Num1 and Num2 - are ASP.NET controls it is prudent to reference the value of the controls' ClientID properties rather than the literal IDs, Num1 and Num2. The reasoning behind this is because the controls' ID values may be modified at runtime if the TextBox appears within a naming container (such as a Master Page).

The call to the Add method passes in four input paramters: the two integer inputs to sum plus a JavaScript function to execute when the call completes (OnComputeSumSuccess) and a JavaScript function to call if there is an error (OnComputeSumError). These code for these two functions follows:

function OnComputeSumSuccess(results) {
   var sumLabel = document.getElementById('sum');
   sumLabel.innerHTML = results;
}

function OnComputeSumError(results) {
   alert('There was an error calling the Add service: ' + results.get_message());
}

The OnComputeSumSuccess references the sum span and sets its innerHTML property to the result returned. The OnComputeSumError function displays the error message in a modal dialog box.

The following screen shots show this functionality in action. When the page is first visited the user interface includes the two textboxes and a "Compute Sum" button.

Start by entering numbers into the two textboxes...

After entering some values, such as 4 and 7, and clicking the "Compute Sum" button, the client-side ComputeSum function runs and sends an HTTP request to the NorthwindServices.asmx Web Service with the following contents:

{"x":"4","y":"7"}

The above JSON is received by the Web Service and deserialized into the input parameter values of 4 and 7, and the Add method is invoked with those values. It computes the sum - 11 - and returns the results back to the client as follows:

{"d":11}

Upon receiving the response, the OnComputeSumSuccess method is called, passing in the value 11 as the return parameter. This value is then displayed as the contents of the <span> element.

The sum is displayed in the span element.

Comparing the Traffic Sent by the Server-Centric and Client-Centric Models


The client-centric model certainly requires more effort to implement than the server-centric model. To implement the Add functionality in a server-centric model we'd make turn the "Compute Sum" button and sum <span> element into a Button Web control and Label Web control, respectively, place these controls in an UpdatePanel, and create a Click event handler for the button, where we'd sum the two numbers and display their output in the Label. But the advantage of the client-centric model is that you have much finer control over the information exchanged between the client and server. Moreover, the sheer quantity of information is usually significantly less.

To appreciate the savings in data afforded by the client-centric model I created two pages with identical functionality, but with one that uses the client-centric model and the other using the server-centric model. Specifically, the page displays the proucts in the Northwind database in a GridView and includes a "Quick Summary Report" user interface that lets the visitor run one of three different queries:

  • How many products cost less than X dollars?
  • How many products cost more than X dollars?
  • How many products cost exactly than X dollars?
Where what operation to perform is chosen from a drop-down list and the value of X is entered via a textbox (see the screen shot below).

The page that implements the server-centric approach uses two UpdatePanels - one for the "Quick Summary Report" section and one for the GridView. Moreover, the two have their UpdateMode properties set to Conditional, ensuring that when one UpdatePanel causes a partial page postback that the other UpdatePanel is not involved in the process. Still, the entire view state is sent back and forth regardless of what UpdatePanel causes a partial page postback, and that view state contains information about controls in both UpdatePanels.

Clicking the "Reveal Answer" button in the page that implements the server-centric model causes a partial page postback and executes the Button's Click event handler. This event handler determines the number of products that meet the specified criteria and then updates the Label control to the right of the Button with the results. Clearly, the only information that needs to be transmitted between the client and server is the selected operation, the dollar amount, and the resulting number of products that apply to that query. However, the server-centric model passes much more information back and forth. On the partial postback over 2,400 bytes of data are sent to the server. (Note: you can use a tool like Fiddler to examine the information sent during a partial page postback; see Troubleshooting Website Problems by Examining the HTTP Traffic for more information on how to use this free tool.)

ScriptManager1=QuickSummaryUpdatePanel%7CRevealAnswer&__EVENTTARGET=&__EVENTARGUMENT=
&__VIEWSTATE=%2FwEPDwULL...&Operation=CostMore&QueryCost=5.00
&AllProducts%24ctl02%24ctl00=on&AllProducts%24ctl11%24ctl00=on

&__ASYNCPOST=true&RevealAnswer=Reveal%20Answer!

The bulk of that data comes from the view state (which I've pared down in the snippet above). And the server responds with over 3,500 bytes of data, which includes the entire markup for the region in the UpdatePanel (even though only the Label's Text property has changed) along with the entire view state.

930|updatePanel|QuickSummaryUpdatePanel|
<p>
   <b>Quick Summary Report:</b>
   <br />
   How many products
   <select name="Operation" id="Operation">
<option selected="selected" value="CostMore">Cost More Than</option>
<option value="CostLess">Cost Less Than</option>
<option value="CostExact">Cost Exactly</option>

</select>
          
   $<input name="QueryCost" type="text" value="5.00" size="8" id="QueryCost" style="text-align: right;" />?
</p>
<p>
   <input type="submit" name="RevealAnswer" value="Reveal Answer!" id="RevealAnswer" />
      
   <span id="QueryAnswer" style="background-color:Yellow;font-size:Large;font-weight:bold;">The answer to your query: 75 products!</span>
</p>
|0|hiddenField|__EVENTTARGET||0|hiddenField|__EVENTARGUMENT||1904|hiddenField|__VIEWSTATE|/wePDwU...

That's a lot of needless information being shuttled between the client and the server. This is the cost of enjoying the ease of use of the server-centric model.

The demo for download at the end of this article includes both server-centric model (discussed above) and a client-centric model implementations. The client-centric model implementation uses calls a Web Service to determine how many products meet the specified criteria and in doing so drastically reduces the overall traffic. First of all, only the necessary information is sent from the client to the server, namely the operation to perform and the cost entered by the user in the textbox. Doing a search for products that cost more than $5.00 results in a request that consumes a mere 43 bytes (instead of over 2,400, as in the server-centric model implementation).

{"operation":"CostMore","queryCost":"5.00"}

And the response was reduced from over 3,500 bytes down to 8 bytes!

{"d":75}

Granted, using the client-centric model puts us "closer to the metal" - we have to write more HTML and more JavaScript - but it grants you more control over the interactions between the client and server and provides a way to minimize the amount of data being exchanged, thereby resulting in a more responsive web application.

To See Script-Enabled Web Services in Action...
To see an example of script-enabled Web services in action be sure to read Periodically Updating the Screen and Web Page Title with ASP.NET AJAX, which shows how to use script-enabled Web services and a touch of JavaScript to interactively update a web page to show information specific to the currently logged on user!

Happy Programming!

  • By Scott Mitchell


    Attachments:


  • Download the Demo Code Used in this Article

    Further Reading


  • Client-Side Web Service Calls with AJAX Extensions
  • Calling Web Services from Client Script
  • Exposing Web Services to Client Script
  • Periodically Updating the Screen and Web Page Title with ASP.NET AJAX
  • Article Information
    Article Title: ASP.NET.Building Interactive User Interfaces with Microsoft ASP.NET AJAX: Retrieving Server-Side Data Using Web Services
    Article Author: Scott Mitchell
    Published Date: January 14, 2009
    Article URL: http://www.4GuysFromRolla.com/articles/011409-1.aspx


    Copyright 2014 QuinStreet Inc. All Rights Reserved.
    Legal Notices, Licensing, Permissions, Privacy Policy.
    Advertise | Newsletters | E-mail Offers