Section 6.5. Asynchronous Programming with Find Service


6.5. Asynchronous Programming with Find Service

When developing applications using Web Service, keep in mind that you are making a network round-trip with every method call, which has serious implications on your application's performance in terms of responsiveness. For example, since calls over the network take a long time to return, you don't want to block the UI thread for your Windows application. This is where the asynchronous programming patterns come to the rescue. Using the .NET framework, it is easy to call Web Service methods asynchronously. So, let's see how you would implement a FindServiceSoap.Find method call asynchronously.

6.5.1. Asynchronous Programming for Windows Applications

When you generate the MapPoint Web Service proxy class using Visual Studio .NET, it also generates the necessary methods for asynchronous programming. For example, if you look for the FindServiceSoap.Find method, you also find the FindServiceSoap.BeginFind and FindServiceSoap.EndFind methods in the proxy class. The Begin and End method pairs enable the asynchronous programming patterns for your web service client applications. Using these methods is really easy; in a synchronous scenario, your Find call looks like the following code:

     //Call the Find Method     FindResults findresults = findsoap.Find(findspec);     //Now display find results     DisplayFindResults(findresults);

If this code is running on the UI thread, it does not get to the DisplayFindResults method until the Find method call completes and returns the findresults value; during this period, users of your application may find it unresponsive. To avoid this situation, create a worker thread and call the Find method using it so that your UI thread is free during this long network round-trip. In fact, that's exactly what the BeginFind and EndFind methods do behind the scenes. To implement the previous code using asynchronous methods, you would do something similar to the following:

First, define a callback method for your asynchronous method calls:

     private void FindServiceCallback(IAsyncResult ar)     {        FindServiceSoap findSoap           = ar.AsyncState as FindServiceSoap;        if(findSoap == null)          return;        FindResults findresults = findSoap.EndFind(ar);        DisplayFindResults(findresults);     }

Next, modify your find call to become an asynchronous BeginFind call:

     //Async call to find     AsyncCallback callback = new AsyncCallback(FindServiceCallback);     findsoap.BeginFind(findspec, callback, findsoap);

The BeginFind invokes the Find method on a different (worker) thread and passes a pointer to the FindSeviceCallback method as a callback method; when the Find method returns a FindResults instance, the callback delegate is invoked so that the FindServiceCallback method gets executed on the UI thread again. In the FindServiceCallback method, you need to obtain the FindResults returned by the Find method by calling the EndFind method and displaying them. Keep in mind that the HTTP session is kept alive during this asynchronous operation behind the scenesthis pattern is asynchronous at your application thread level but not at the HTTP communication level.

6.5.2. Asynchronous Programming for Web Applications

Multithreaded programming works well for Windows applications if you are calling web services, but wouldn't it be nice to adopt this asynchronous programming model for web applications as well? Wouldn't it be convenient to develop more responsive applications without doing a complete page refresh? You can do these things with a combination of JavaScript and Msxml2.XMLHTTP ActiveX control. The use of JavaScript with asynchronous XML messaging is called Asynchronous JavaScript and XML, or simply AJAX. While AJAX terminology is fairly new to web application development, the use of JavaScript and XMLHTTP is not. In this section, I will go over some scenarios where AJAX can be used in your web applications to improve the overall user experience.

6.5.2.1. AJAX-Enabling Your Web Applications

In theory, AJAX is no different from any other web application that uses HTTP request and responsehowever, adding asynchronous calls from your web page to the web server that uses JavaScript dramatically improves the user's experience.

To understand how to leverage AJAX in your MapPoint Web Service web applications, you need to understand how AJAX works, which is explained in the following section.

For AJAX to work, you need three core components:

  • A web page (htm, aspx, etc.) that hosts JavaScript containing asynchronous calls to the web server

  • XMLHTTP ActiveX control enabled from the client web browser

  • A server-side component that can process HTTP GET requests using the query string parameters

All three of these components together make an AJAX implementation. Usually, the server-side component is an HTTP Handler, since it renders only the script instead of conventional HTML. An HTTP Handler is similar to an ISAPI extension, and you need to implement the IHTTPHandler interface to develop an ASP.NET HTTP Handler. These concepts are shown pictorially in Figure 6-3.

Since designing a web page and writing JavaScript are straightforward tasks, let me delve into the server-side components that are required for AJAX.

For example, you want to develop a web application that implements "Find a place" functionality; in this application, when users search for a place, generally MapPoint Web Service comes back with a list of possible matches to be displayed for disambiguation. Conventionally, you would implement this process using the following series of actions:

Figure 6-3. AJAX architecture for MapPoint Web Service applications


  1. Have the user type in a place and click a button to find it.

  2. Post the request to the server page that invokes the MapPoint Find Service calls to find the place.

  3. If there is more than one match to the input string, display them in a list box where the user can select the place that he is looking for.

  4. Post that selection back to the server page that invokes MapPoint Render Service to get a map.

The same application can be implemented with AJAX to improve the overall user experience:

  1. As the user types each character into the input place textbox, make an asynchronous call to the server to fetch matching places and display them in a dynamic drop-down list.

  2. Have the user select the place she is looking for.

  3. Post that selection back to the server page to display a map.

An implementation of this application results in the user interface shown in Figure 6-4.

The experience of using the application is far richer with AJAX when compared to a traditional MapPoint find web application. Next, let's see how to implement this application.

Figure 6-4. Place lookup using MapPoint Web Service and AJAX


6.5.2.2. Implementing MapPoint Lookup AJAX Application

The implementation of this application consists of developing the following:

  • The web page that hosts JavaScript that makes async calls to the server

  • An HTTP Handler that processes async requests and returns proper JavaScript

  • JavaScript that makes async calls.

Let's look at each step in detail.

Developing the web page. In this step, simply create an ASPX page that has the input place textbox with the following event wired up:

     <input  onkeyup="DoLookup()" type="text" size="56">

The input textbox "place" has the onkeyup event wired up to the DoLookup() method, which will be called each time the user types in a character.

Also define a div element to hold the results; this element acts as a dynamic list box to show results. If no results are found, the div is hidden as an invisible element on the page, and if search results are found, the div is visible for the user to select the desired find result:

     <div  UNSELECTABLE='on' style="VISIBILITY: hidden;"></div>

The next step is to implement the HTTP Handler that processes find requests on HTTP GET.

Developing the HTTP Handler. To develop an HTTP Handler, you need to implement the System.Web.IHttpHandler interface, which requires overriding the ProcessRequest method that handles the incoming request.

You can read more about implementing HTTP Handlers on MSDN at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemwebihttphandlerclassprocessrequesttopic.asp?frame=true.


In this method, you check an incoming request's query string to see whether there is any place name; if a place name exists, make a FindServiceSoap.Find method call to return matching find results. The results need to be formatted so that the calling JavaScript can understand and display the contents. So, I decided to do the result formatting on the server side in the HTTP handler and simply return the find results that are display-ready. The following code shows the ProcessRequest method implementation:

     public void ProcessRequest(HttpContext context)     {         //See incoming place query string parameter         string input = context.Request["place"] as string;         if(input == null || input == string.Empty)         {             context.Response.Write("var invalidInputFound = 0;");             return;         }         //If there is a valid input call MapPoint Web Service         try         {             //See if this has a cache item:             string response = context.Cache[input] as string;             if(response != null)             {                 context.Response.Write("ShowLocation(" + response + ");");                 return;             }             else             {                String[] findings = null;                //Call MapPoint Web Service and get the place names into                //the findings string array                 . . . .                //Now write back appropriate JavaScript                 if(findings == null || findings.Length == 0)                 {                     context.Response.Write("var novalidOutputFound = 0;");                     return;                 }                 System.Text.StringBuilder sb = new System.Text.StringBuilder();                 int count = findings.Length;                 sb.Append("\"");                 for(int i =0; i< count; i++)                 {                     //Open div and add id                     sb.Append("<div id='findresult_" + i.ToString() + "' ");                     //Add unselectable = on                     sb.Append(" UNSELECTABLE='on' ");                     //Add findresult content                     sb.Append("<img align=absmiddle src=arrow.gif border=0> " +                                 findings[i].Trim().Replace("'", "\'"));                     //close div                     sb.Append("</div>");                 }                 sb.Append("\"");                 //Write back the necessary JavaScript and                 //add this entry to cache so that we don't have to hit MapPoint                 //Web Service                 if(sb.Length > 5)                 {                     //Write JavaScript                     context.Response.Write("ShowLocation(" + sb.ToString() + ");");                     //Add to cache                     context.Cache.Add(input, sb.ToString(), null,                                      System.DateTime.MaxValue,                                      TimeSpan.FromHours(6),                                      System.Web.Caching.CacheItemPriority.Default,                                      null);                 }                 else                 {                     context.Response.Write("var zeroOutputFound = 0;");                 }             }             return;         }         catch(Exception ex)         {             context.Response.Write("var errorMesage = '" + ex.Message + "';");         }     }

This HTTP handler writes appropriate JavaScript back to the client as a response to any incoming request. For example, an incoming request such as the following:

     http://yourHTTPHandlerUrl?place=New%20York

results in the following JavaScript being sent back as response:

     ShowLocation("<div id='findresult_0'  UNSELECTABLE='on' ><img align=absmiddle     src=arrow.gif border=0> New York, New York, United States</div><div id='findresult_1'     UNSELECTABLE='on'><img align=absmiddle src=arrow.gif border=0> New York (state),     United States</div><div id='findresult_2'  UNSELECTABLE='on'><img align=absmiddle     src=arrow.gif border=0> New York (county), New York, United States</div><div     id='findresult_3'  UNSELECTABLE='on'><img align=absmiddle src=arrow.gif border=0> New     York, Santa Rosa, Florida, United States</div><div id='findresult_4'     UNSELECTABLE='on'><img align=absmiddle src=arrow.gif border=0> New York, Wayne, Iowa, United States</div>");

You can also simply return an array of strings in JavaScript and let the client code do the formatting for the display, but I chose to implement it on the server side since it simplifies the JavaScript implementation.

The next step is to develop the JavaScript to glue these pieces together.

Developing the JavaScript. You might have noticed a couple of JavaScript functions that I have been using so far:


DoLookup

Captures the user's key inputs and sends an asynchronous call to the HTTP Handler.


ShowLocation

Displays the find match results to the user in a dynamic drop-down list.

Let's see how to implement these two functions.

The DoLookup function uses XMLHTTP functionality to send the request that we developed in the previous step asynchronously to the HTTP handler; a simplified version of DoLookup is shown in the following code:

     //Define a global variable to hold http request     //object     var xmlhttp=null;     Function DoLookup()     {         //Create a valid url with user typed place value         var url = "your http handler url"? + place.value;         //Create an instance of xmlHttp         xmlhttp=new ActiveXObject("Msxml2.XMLHTTP");         if(xmlhttp)         {             //Now open a request and assign callback             xmlhttp.open("GET",url,true);             xmlhttp.onreadystatechange=FindPlaceRequestCallBack;             //Send the request             xmlhttp.send(null);         }     }

A callback function, FindPlaceRequestCallBack, is assigned to handle the response and any other state changes in the input request; the implementation of the FindPlaceRequestCallBack is as follows:

     function FindPlaceRequestCallBack()     {       if(xmlhttp)       {         if(xmlhttp.readyState==4&&xmlhttp.responseText)         {            //Get the response content            var placecontent = xmlhttp.responseText;            //Just execute the response content            eval(placecontent);            xmlhttp = null;         }       }     }

The callback function receives the response text from the HTTP handler and executes the resulting JavaScript using the eval method. The resulting JavaScript is nothing but the ShowLocation function call with Find result matches for the input place. Next, we need to implement the ShowLocation function:

     function ShowLocation(findResults)     {         if(!findResults)         {             return;         }         //Display results         if(document.getElementById(displayplanelname))         {           document.getElementById(displayplanelname).style.visibility="visible";           document.getElementById(displayplanelname).innerHTML = findResults;           document.getElementById("place").focus();         }     }

In this function, the results are assigned to the div element to be displayed to the user. Your AJAX application for place lookup is now ready. With minimal effort, you can AJAX-enable your MapPoint Web Service web application to provide a great user experience.

Obviously, you can extend these functions more to optimize your client-server communication by limiting the requests to strings more than three characters in length. Also, you can implement more features such as scroll enabling with arrow keys, and mouse-select enabling in JavaScript to add more depth to the application. The sample application included in the companion material has all these features implemented in JavaScript.

Finally, because you are charged a fee for each MapPoint Web Service Find call, you need to evaluate this design appropriately; a more cost-effective variation to this implementation might be to do a lookup asynchronously when the user clicks Enter, instead of performing a lookup for each character typed.




Programming MapPoint in  .NET
Programming MapPoint in .NET
ISBN: 0596009062
EAN: 2147483647
Year: 2005
Pages: 136
Authors: Chandu Thota

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net