Using WebServices with ASP.NET

Webservices without tears? Popup maps in your websites? Word-completion? Ajax-based Search-engines? No problems, with Daniel's sample application to guide you.

SOAP Webservices provide a simple way of providing public information in websites. Directories, maps, translations, searches and zipcode/postcode lookup all suddenly become quick and easy to implement, especially in .NET. Windows Live Webservice have now joined the market with a whole range of services which are likely to change the way we use the Web.

In this article I will show off the abilities of ASP.NET 2.0 to consume the Windows Live Search Webservice and use AJAX 1.0 and the AjaxControlToolkit to replicate some of the features in Microsoft’s search engine Live Search. I am going to take a more advanced approach to the solution I wrote for the Code Project and provide a richer overall experience in this demo. I’ve provided the source too so you can try it yourself, and experiment

This article will demonstrate the following:

  • Consumption of the Windows Live Search Webservice and the ability to bind these results to an ASP.NET Gridview control
  • The use of AJAX 1.0 to search and page through the search results
  • Using the AjaxControlToolkit and an online dictionary web service to create an auto complete function that aids in finding search words
  • Tapping into the Live Search spelling suggestion service to display a polite message to the user of a possible spelling suggestion for their misspelled word(s)
  • Enhancing the search experience with a Live.Local lookup to display listings out of the yellow pages with their associated local addresses
  • Taking the local addresses and providing a dynamic Microsoft Virtual Earth map of the location to include all the power and enhanced features associated with the Virtual Earth API.

Some of the advantages of using these web services include:

  • A full blown SOAP Webservice allowing you to take control of the data
  • The ability to search multiple websites from your company and / or its partners
  • Not having to worry about indexing the content of your company’s global website(s)
  • Harnessing the power of Live Search’s advanced search features in your queries
  • Using Virtual Earth to map out streets in your local area.

Some of the limitations include:

· A limit of 10,000 queries a day to use the service for free. (Note: Normally each page in the Gridview Control will fire a new query even though the search query is the same. This demo makes use of sessions to prevent this from happening. – Optional in web.config)

· You can only return a maximum of 50 results per query.

Getting started

Setting up the environment

The first steps are to set up your demo/development environment. This demo was created in Windows XP Pro using IIS. The following actions need to occur in order to run this demo locally.

  1. Install ASP.NET 2.0 (http://www.windowsupdate.com/)
  2. Install ASP.NET 2.0 AJAX Extensions 1.0 (http://asp.net/ajax/downloads/archive/)
  3. Download and Setup the demo application in IIS and choose the ASP.NET 2.0 framework
  4. Obtain a Key from Microsoft to use this Webservice
  5. Add the key to the appropriate app setting in web.config
  6. (Optional) Follow the additional instructions in web.config to customize your output.

It is suggested that you open this website up in Visual Studio 2005 or greater and take advantage of IntelliSense and explore the methods, properties and various classes that make up the Live Webservice and this demo.

Setting up web.config

The app key for this web service

Get the key from here… https://msdn.microsoft.com/en-us/dn308572.aspx
and insert it into Add the appropriate app setting in web.config

The dictionary service for autocomplete

Choose a dictionary from here: AonaWare’s DictService
In this demo I use WordNet (r) 2.0 (wn)

The Windows Live Search expression

By default I leave this key blank to search the entire Internet but you could use an expression such as:

This expression can be built using the Advance Search Feature of Windows Live (http://www.live.com/). It basically states to make the search have 100% fresh content with a popularity of 100% and search the sites in parenthesis with the exception of the council meeting minute’s directory.

The Results Size

The limit is 50 results. By default I set the results size to 25.

Logging

 

Most errors get encapsulated in the AJAX 1.0 abyss. To make things easier on me when developing this application I created an optional logging feature. By default I have this turned off, but if you decide to set this to true then you must provide a physical path to write the log files.

Location for Local.Live Services

Add the longitude and latitude of your local area. The default location of this demo is Seattle, WA with a radius of 25 miles. The values should be written as doubles.

Sessions

Use this key to turn sessions on or off. Sessions will help you save your available queries. MS gives you a 10,000 daily limit. With paging turned on in the GridView Control, each time a user selects a new page it will constitute a new search query if sessions are turned off. Turn the session switch on to avoid this. By default I have sessions turned on.

Exploring the WindowsLiveSearch class

Items to consider

On a security note, the possibility of cross site scripting getting stored in the results became a real problem. This would not only wreck havoc on your presentation layer markup, but more importantly it could ultimately send your users to dangerous and inappropriate places on the Internet. To solve this dilemma I referenced Microsoft’s AntiXssLibrary.dll in the class. I then called the AntiXss.HtmlEncode() function for each result returned by the webservice. This will strip cross site scripts and secure your output for the client.

When consuming the results from the Webservice I immediately ran into null reference exceptions for fields that ultimately did not exist. For example, a result set usually contained the usual fields such as the title, URL, description, etc. If by chance no information was indexed for the description field, the webservice returned null for that particular item. I created a function called CheckforNull that simply returned an empty string if a null was present. That solved that problem.

Another item of consideration was the use of sessions. Storing objects in sessions isn’t a good idea for a number of reasons, but when you weigh the alternative it doesn’t seem so bad anymore. The limit of free queries a day by Microsoft is 10,000. Not so bad, huh? Well let’s say you have one user who really wants to find something and makes five queries and you display five results a page for the maximum 50 results total. This user also decides to look at all 10 pages of results for each of the five queries. Well instead of five queries, you now have used 50 for just that one person. And if that person wanted to page backwards because they thought they missed something then it would only add to the queries because each page clicked in the Gridview control fires another instance of the search and creates another query. That is definitely a worse case scenario, but websites that receive a lot of traffic have to take these things into consideration. With sessions active, five queries will remain five queries. For those who ultimately hate the idea of storing an object in a session then you can always turn sessions off in web.config as stated earlier in this article.

The Search Method

Of course the main method is the search method.

Figure 1 – The main entry point of the class
public IList<LiveSearchResults> Search(string searchQuery)
        {
            // Basic checks
            if ((searchQuery == null) ||
                (searchQuery.Length == 0) ||
                (searchQuery.Trim() == “”))
                return null;
 
            IList<LiveSearchResults> webCollection = new List<LiveSearchResults>();
 
            using (MSNSearchService s = new MSNSearchService())
            {
                SearchRequest searchRequest = new SearchRequest();
                searchRequest = SetUpRequest(searchRequest, searchQuery, SearchProperties);
                SearchResponse searchResponse;
                try
                {
                    // If the searchQuery is the same
                    // Session flag checked in function
                    if (IsSameSearch(searchQuery))
                        searchResponse = (SearchResponse)HttpContext.Current.Session[“searchResponse”];
                    else // Use Live Search to get the Response
                        searchResponse = s.Search(searchRequest);
 
                    // Session flag checked in function
                    GenerateSession(searchResponse, searchQuery);
                    webCollection = CaptureWebResults(searchResponse);
                }
                catch (Exception e)
                {
                    ErrorMsg = e.ToString();
                }
                finally
                {
                    // If there was an error
                    // Logging flag checked in function
                    if (ErrorMsg.Length > 0)
                        LogMessage(“There was an error with searchQuery: ” +
                            searchQuery);
                    else
                        LogMessage(“A successful search was made with searchQuery: ” +
                            searchQuery);
                }
            }
 
            return webCollection;
        }

This is all the client application is concerned with. In the order of operations, this is how the webservice is consumed via this function.

  1. It creates an instance of the MSNSearchService
  2. It creates a SearchRequest object
  3. It calls a local method to set up and store all the setup information into the newly created SearchRequest object
  4. It creates a SearchResponse object
  5. It queries and stores the search results into the searchResponse object. (s.Search is the query itself)
  6. It sends the searchResponse off to another local method called CaptureWebResults to make a generic list of the results.
  7. Then it hands that list off to a local list object called webCollection and returns the webCollection to the client.
  8. In this case, the client then binds the webCollection to a Gridview control.

Those eight items are essentially all you need to know about the logic of consuming the MSNSearchService in ASP.NET.

You then have the optional session logic in there and the optional logging logic. The IsSameSearch method is a local method to check whether the query is the same. At the same time there is a check with web.config to see if sessions are on. If all is a go, it pulls the searchResponse from the session instead of firing s.Search again, which will create another query. This will ultimately spare your 10,000 query daily limit from getting consumed so fast!

The LogMessage method has a flag that checks whether logging is turned on. ErrorMsg is a public property of the class and is exposed in the LogMessage function.

The main local methods of this class are:

  • SetUpRequest – Here you have the opportunity to let Windows Live know what and how you want your results to be returned. I set up three main Requests in this function. The first is the obvious web request. The second request is the spelling request, which will return the misspelled words. The third request is the Phonebook request for all those Live.Local results.
  • CaptureWebResults – This is the heart of the class. It takes everything in the web service response and stores the data in generic lists. Here we also secure the data and ensure there are no null values. This method fires up another method to handle any spelling suggestions.
  • HandleSpellingSuggestion – This method I got straight from the LiveSearch sdk. It pulls the response from the response object and then turns it into a readable string that’s ready for printing.
  • CheckForNull – As mentioned earlier in this article, a search response may contain null fields. This method ensures there will be no null values stored in the list.

The Web Client

There are many ways a client can go about handling the search method. I decided to go the ObjectDataSource method of handling it. I also decided to use the AJAX 1.0 library for searching and paging and the AJAXControlToolkit for the autocomplete functionality.

The ObjectDataSource

Just writing that makes me want to cringe. I guess it wasn’t as scary as I originally thought. The main challenge was figuring out how to expose the public properties to the web client for the right instance of the class.

So after much trial and error I tried:

Figure 2 – Getting the instance of the class WindowsLiveSearch thisObject = new WindowsLiveSearch();
  protected void ObjectDataSource1_ObjectCreated
(object sender, ObjectDataSourceEventArgs e)
  {
    thisObject = (WindowsLiveSearch)e.ObjectInstance;
  }

Now I have the instance of the class used by the ObjectDataSource, and I can expose the public properties to the Web Client.

Another road block came when I wanted to get the total count of the results set. The ObjectDataSource has a Rows.Count method but that only returns the count of the visible rows on the screen, not the entire results set.

So to solve this I used this logic in the ObjectDataSource1_Selected method:

Figure 3 – Getting the return value    try
    {
      IList<LiveSearchResults> resultsCollection = new List<LiveSearchResults>();
      resultsCollection = (IList<LiveSearchResults>)e.ReturnValue;
      resultsTotal = resultsCollection.Count;
      if (resultsTotal == 0)
        Instructionlbl.Text = “Your search provided no results.”;
    }
    catch (System.NullReferenceException)
    {
      Instructionlbl.Text = “Please enter a search term.”;
    }  

I first created a local list object of the LiveSearchResults Type. This type contains public properties that represent the web results returned by the webservice. I used the ObjectDataSourceStatusEventArgs e to get the return value. The return value is what the Search method returns, a generic list. So then I take that list and get the count.

This not only gave me the opprotunity to get the total results count from the generic list, but now I can detect whether a search provided results or whether a search was even performed at all! So I threw an instruction label on the web form to print out those messages to the user.

Figure 4 – Handling Spelling    if (thisObject.SpellingSuggestion.Length > 0)
    {
      Spellinglbl.Text = “Did you mean “
        + “<a href=’javascript:void(0);’ onclick=’submitSpellingSuggestion()’ title='”
        + thisObject.SpellingSuggestion
        + “‘>”
        + thisObject.SpellingSuggestion
        + “?</a>”;
      SpellingSuggestion.Text = thisObject.SpellingSuggestion;
    }
    else
      Spellinglbl.Text = “”;

Still in the ObjectDataSource1_Selected method I check to see if the spelling suggestion has length. If there is a suggestion, I throw it inside a label..

Figure 5 – Handling the Phone Book Results// Get the Phone Results and bind them
    PhoneResultsView.DataSource = thisObject.PhoneResults;
    PhoneResultsView.DataBind();
    thisObject.Dispose(); // Dispose of this instance

Since I have access to my public properties, I decided to store the PhoneResults in one via the class. I then bind that property to the PhoneResults Gridview Control. I am now done with the class object so I dispose of it..

AJAX AJAX AJAX

My mother thinks I invented a way to clean websites when I mention that I use AJAX in my web applications. My wife can tell anyone I don’t like to clean! So what am I referring to? Well the AJAX library from Microsoft of course! With a few server tags you can be implementing some powerful AJAX features. In this application all I did was surround the portion of the page I wanted updated in the AJAX 1.0 UpdatePanel tag.

Figure 6 – The AJAX 1.0 UpdatePanel tag<asp:UpdatePanel ID=”UpdatePanel1″ runat=”server”>
… The Gridview for the web results
… The Gridview for the phonebook results
… The instruction label
… The spelling label
… yada yada yada!
</asp:UpdatePanel>

 

 

That is all there is to that! Thanks Microsoft!

Figure 7 – AutoComplete
460-daniel1.jpg

The AJAXControlToolKit and the AutoComplete function

This was the fun part of the application. What happens when you mix an online dictionary web service with the AJAXControlToolKit? Well nothing, unless you take the AutoCompleteExtender and put it to use. I basically just copied over the example Microsoft made in the toolkit itself and consumed an online dictionary web service instead. So the logic is this. When you finally type at least 3 letters in the search field the AutoCompleteExtender will call a local webservice to handle the operation. This local service then calls the dictionary web service over the Internet and returns words based upon the 3 letter prefix you provided. Cool, huh? Well to make things even cooler you can choose different dictionaries to get different results. This setting is configured in web.config.

Virtual Earth API

To top things off, I decided to add one more piece to this solution: Virtual Earth. The Virtual Earth API offers a way for map enthusiast to display maps via JavaScript. What I wanted to do was present the Live.Local results in a way that a user could mouse over them and get a map of the address in the results. So where would I display this map? To solve this problem I downloaded the latest version of overlibmws, which was spawned from the original overlib library created by Erik Bosrup.

Figure 8 – The overlibmws popup displaying a virtual earth map
460-VirtualEarth.jpg

After mousing over a result on the right, a JavaScript call throws the address up to the overlibmws handler. This simple JavaScript function throws an iframe in the popup bubble which contains all the logic necessary to search for the local address and display the map. The iframe is actually a .NET webform that takes the address in the form of a QueryString and inserts it into the client side Virtual Earth code. On the screen you can take advantage of all the powerful features Virtual Earth provides, including a 3d view (After a small installation) and Bird’s eye view of the location.

You can easily use Google Maps API instead, but I found Virtual Earth to be more powerful and useful than Google Maps and at the last minute I made a call to use Virtual Earth.

Summary

There are a number of ways to use Live Webservices to customize your own solutions. Because the Search API is a full blow SOAP webservice you have control of the data. The interoperability of Windows Live Services and ASP.NET allows you to easily incorporate a number of features in a short amount of development time. In short, there are a number of ways these services can benefit businesses and organizations by helping users find data faster and helping developers looking for a quick solution to save a few keystrokes.