• home
  • forum
  • my
  • kt
  • download
  • Web services

    Author: 2007-07-05 09:00:04 From:

    After completing this tutorial, you will be able to
    • Understand the importance of Web services
    • Use the technologies underlying Web services
    • Write Web services using ASP.NET
    • Consume Web services

    This tutorial covers Web services from an ASP.NET perspective. Over the last few years ¡°Web services¡± has emerged as a buzzword for enabling the next generation of computer connectivity. While networking a bunch of computers together isn't trivial, it's generally a solved problem these days. Most workplaces in the modern world depend upon an internal network of computers to allow the people staffing the enterprise to communicate and work effectively

    After completing this tutorial, you will be able to

    • Understand the importance of Web services

    • Use the technologies underlying Web services

    • Write Web services using ASP.NET

    • Consume Web services

    This tutorial covers Web services from an ASP.NET perspective. Over the last few years ¡°Web services¡± has emerged as a buzzword for enabling the next generation of computer connectivity. While networking a bunch of computers together isn't trivial, it's generally a solved problem these days. Most workplaces in the modern world depend upon an internal network of computers to allow the people staffing the enterprise to communicate and work effectively.

    With the rise of the internal company network comes the desire to tie machines together programmatically as well. That is, a program on one machine should be able to call program methods on another machine without human intervention. Many enterprises spent nearly the entire final decade of the twentieth century trying to get their computers to talk to one another programmatically. On the Microsoft platform, this was usually done with Distributed COM (DCOM).

    The next step in connecting computers is happening over the Internet. There's already a ubiquitous connection available (computers connected via HTTP) and a well-understood wire format (XML). Together, these two elements make up Web services.

    The desire to call software methods ¡°over there¡± from ¡°over here¡± has been around ever since the advent of distributed computing networks. Beginning in the days of Remote Procedure Calls all the way through the latest version of Distributed COM (DCOM), the promise of remoting has been to exercise a network of computers to solve computing problems rather than pinning the whole problem on a single computer.

    Remoting involves several fundamental steps:

    1. The caller flattens the call stack into a stream that may be sent over the wire.

    2. The caller sends the serialized call stack across the wire.

    3. The endpoint receives the serialized call stack and turns it into a usable call stack on the server.

    4. The endpoint processes the method call.

    5. The endpoint transmits the results back to the caller.

    Figure 19-1 Illistrates the general remoting architecture employed by most remoting systems.
    Figure 19-1 Illistrates the general remoting architecture employed by most remoting systems.

    Several different network remoting technologies have emerged over the last decade, including DCOM and CORBA among others. (CORBA is an acronym for Common Object Request Broker Architecture¡ªa remoting technology prevalent on other operating systems). It doesn't matter if the remoting framework is DCOM, CORBA, or even the .NET remoting services¡ªthe fundamental steps of remoting remain the same. For example, in DCOM the client talks to a component named the proxy, whose job it is to flatten the call stack and send it on its way. On the server side, a component named the stub receives the network packets and turns the incoming stream into a real call on the server. If the framework is .NET remoting, then the term for the proxy component is the ¡°transparent proxy.¡± The transparent proxy talks to the real proxy, which sends the bytes across the network. Once at the server, a component named the sink unpacks the bytes and turns them into a real call.

    Web services work much the same way. The fundamental remoting steps are all there. However, this time around the wire format is an XML format formalized as SOAP and the connection protocol is HTTP.

    Over the last 18 tutorials we've looked primarily at how ASP.NET makes it easy to handle a wide variety of Web application scenarios. We've seen that ASP.NET handles GET and POST methods, redirecting the request to a handler. Up until now the job of the handler has been to process the incoming query string and render some output generally intended for human consumption. Developing a Web service is all about writing an application intended for consumption by another program.

    Web services are Internet endpoints available most commonly through HTTP and HTTPS (Hypertext Transfer Protocol Secure). The job of a Web service is to consume HTTP requests containing XML payloads formatted as SOAP. The messages have a specific schema applied to them, which in effect may be thought of as a transportable type system. Web services are also responsible for providing metadata (Web Service Description Language) describing the messages they consume and produce.

    Simple Object Access Protocol (SOAP)

    While it seems obvious that the Web is an excellent medium for distributing a user interface¨Coriented application to the masses, it may not seem so obvious that the same technology might be used to make method calls. One of the main reasons Web services may exist now is because different enterprises can agree upon what a method call looks like, and they can all access it over already-existing HTTP connections.

    Web service method calls are encoded using XML. The format that callers and services agree on is known as Simple Object Access Protocol (SOAP). The SOAP protocol is an XML formalization for message-based communication. SOAP defines how to format messages, how to bind messages over HTTP, and a standard error representation.

    Transporting the Type System

    The primary interoperability focus of Web services is to widen the audience of an application so that as many clients as possible can invoke methods of the service. Because the connective medium involved is the Internet, any computer that can invoke HTTP requests becomes a potential client. Paired with the ability to connect over HTTP and to format calls as XML SOAP messages, a client can make calls to any of your Web service's methods.

    With the focus on interoperability between as many platforms as possible, it becomes very important that the caller and the service agree on the data types being passed back and forth. When a client calls a method containing parameters, the two endpoints might each have their own way of understanding the parameter types. For example, passing a character string between two .NET endpoints does not pose a big problem. However, passing a string between a client running a non-.NET platform and a service written using .NET does pose a problem because a character string type is almost certainly represented differently on each platform.

    When calling methods between two computers using HTTP and XML, it's very important that a schema is provided on each end so that the parameter types are interpreted correctly. Fortunately, this detail has been pushed down into the Web service tools that are currently available now.

    Web Service Description Language

    Given a connection protocol (HTTP) and wire format (XML + SOAP), the final ingredient making Web services a viable technology is the notion of a service description. Even though two endpoints agree on the connection protocol and the wire format, the client still has to know how to set up the call to a service.

    Services advertise their capabilities via another XML formalization named Web Service Description Language (or WSDL as it's commonly called). WSDL specifies the target URL of the service, the format in which the service expects to see methods packaged, and how the messages will be encoded.

    Just as there's nothing stopping you from writing code to handle HTTP requests from scratch, you could handle Web service requests from handwritten code. You could write a Web service armed with a only a decent XML parser and a socket library (for communicating over your server's communication ports). The work involved includes the following:

    1. Listening to port 80 to receive method calls

    2. Parsing the incoming XML stream, unpacking the parameters

    3. Setting up the incoming parameters and performing the work

    4. Packing a suitable XML SOAP response and sending it to the caller

    5. Advertising the service's capabilities via WSDL

    After the second or third time implementing a service by hand, you'd probably come to the following conclusion. Much of the work involved in making a Web service work is repetitive and might as well be pushed into a library. That's exactly what ASP.NET does. ASP.NET will handle the details of making a Web service through the System.Web.Services.Service class.

    ASP.NET handles Web services with a limited amount of programming effort. Remember how the ASP.NET pipeline architecture works. Requests coming from clients end up at the server's port 80. ASP.NET Web services live in a file type named with the extension .asmx. If the server is running ASP.NET, IIS routes the request for files with the ASMX extension to ASP.NET, where they're handled like any other request.

    ASP.NET includes an attribute named [WebMethod] that maps a SOAP request and its response to a real method in a class. To make the service work, you simply derive a class from System.Web.Services.Service and expose methods using the [WebMethod]. When the request comes through, the target class will be ¡°bound¡± to the .asmx endpoint. As with normal page execution, the current HttpContext is always available. In addition, ASP.NET automates WSDL generation, and Microsoft provides tools to automate generating client-side proxies given the WSDL.

    The following example illustrates a Web service that retrieves quotes from the quotes collection we saw in Tutorial 11 (¡°Databinding¡±), Tutorial 14 (¡°Application Data Caching¡±), and Tutorial 17 (¡°The Application and HTTP Modules¡±). This example will expose the quotes collection via a set of methods expressed as a Web service.

    Write an ASP.NET Web Service

    1. Create a new Web site project. Name the project WebServicesORama. Make it an HTTP site that uses IIS.

    2. Rename the class from Service to QuoteService. Rename the code file from Service.cs to QuoteService.cs. Rename the ASMX file from Service.asmx to QuoteService.asmx.

    3. After Visual Studio is done, you'll get a stubbed-out Web service that looks like this:

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

      You'll also get an ASMX file. The Web service handler (named ASMX, with ¡°M¡± standing for method) works very much like the ASPX page handlers and the ASHX custom handlers. When clients surf to the ASMX page, IIS redirects the request to the ASP.NET ISAPI DLL. Once there, ASP.NET compiles the code associated with the ASMX file and runs it just as it would any other HTTP handler. Here's what the ASMX file looks like. There's not much here. Most of the code lies within the accompanying code file.

      <%@ WebService Language="C#"
      CodeBehind="~/App_Code/Service.cs" %>
    4. Surf to the QuoteService.asmx file to see what a default GET renders:

      Graphic

      By default, ASP.NET renders the names of the available methods when you just GET the ASMX file. Notice that the HelloWorld method (provided by Visual Studio) is exposed. If you want to try running the HelloWorld method, you can click on the HelloWorld link, which renders a new page with a button you can click to invoke the method.

      Graphic
    5. Before adding any code, click the Service Description link. The Web service will send back the WSDL for the site. You can page through it to see what WSDL looks like. This data is not meant for human consumption, but rather for client proxy generators (which we'll examine shortly).

      Graphic
    6. To have some quotes to expose as Web methods, import the QuotesCollection from Tutorial 14. The project name is UseDataCaching. Highlight the App_Code node within the solution explorer. Select Web Site | Add Existing Item from the main menu and find the file QuotesCollection.cs. In addition to importing the QuotesCollection.cs file, grab the QuotesCollection.xml and QuotesCollection.xsd files from the UseDataCaching\App_Data directory and place them in the App_Data directory for this project.

    7. Write a method to load the QuotesCollection. Check first to see if the QuotesCollection is in the cache. If not, create a QuotesCollection object and load it using the quotescollection.xml and quotescollection.xsd files. Load the quotes into the application cache during the construction of the QuoteService class. When you add the data to the cache, build a dependency upon the quotescollection.xml file. One of the Web methods we'll add will modify the XML file, so we'll want to flush it from the cache when it's updated.

      using System;
      using System.Web;
      using System.Data;
      using System.Web.Services;
      using System.Web.Services.Protocols;
      using System.Web.Caching;
      
      [WebService(Namespace = "http://tempuri.org/")]
      [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
      public class QuoteService : System.Web.Services.WebService
      {
      
         QuotesCollection LoadQuotes()
         {
            QuotesCollection quotesCollection;
      
            HttpContext ctx = HttpContext.Current;
            quotesCollection = (QuotesCollection)ctx.Cache["quotesCollection"];
            if (quotesCollection == null)
            {
               quotesCollection = new QuotesCollection();
               String strAppPath = Server.MapPath("");
      
               String strFilePathXml =
                  strAppPath +
                  "\\app_data\\QuotesCollection.xml";
               String strFilePathSchema =
                  strAppPath +
                  "\\app_data\\QuotesCollection.xsd";
      
               quotesCollection.ReadXmlSchema(strFilePathSchema);
               quotesCollection.ReadXml(strFilePathXml);
      
               CacheDependency cacheDependency =
                  new CacheDependency(strFilePathXml);
      
               ctx.Cache.Insert("quotesCollection",
                        quotesCollection,
                        cacheDependency,
                        Cache.NoAbsoluteExpiration,
                        Cache.NoSlidingExpiration,
                        CacheItemPriority.Default,
                        null);
            }
            return quotesCollection;
         }
          public QuoteService() {
          }
      
          [WebMethod]
          public string HelloWorld() {
              return "Hello World";
          }
      }
    8. Now write a method that gets a random quote from the table and send it back to the client. The QuotesCollection class inherits from the DataTable class, which is a collection of DataRows. Unfortunately, returning a DataRow from a Web method doesn't work because DataRow doesn't have a default constructor. So instead, add a new struct to the Web service that wraps the quote data. That is, a struct that contains strings for the quote, the originator's first name, and the originator's last name.

      Name the method for fetching a quote GetAQuote. Have GetAQuote load the quotes using LoadQuotes. The GetAQuote method should generate a number between zero and the number of rows in the QuotesCollection, fetch that row from the table, wrap the data in a Quote structure, and return it to the client. Be sure to adorn the GetAQuote method with the [WebMethod] attribute.

      using System;
      using System.Web;
      using System.Data;
      using System.Web.Services;
      using System.Web.Services.Protocols;
      using System.Web.Caching;
       public struct Quote
       {
         public String _strQuote;
         public String _strOriginatorLastName;
         public String _strOriginatorFirstName;
      
         public Quote(String strQuote,
                      String strOriginatorLastName,
                      String strOriginatorFirstName)
         {
             _strQuote = strQuote;
            _strOriginatorLastName = strOriginatorLastName;
            _strOriginatorFirstName = strOriginatorFirstName;
         }
      }
      [WebService(Namespace = "http://tempuri.org/")]
      [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
      public class QuoteService : System.Web.Services.WebService
      {
      
      
         QuotesCollection LoadQuotes()
         {
            QuotesCollection quotesCollection;
      
            HttpContext ctx = HttpContext.Current;
            quotesCollection = (QuotesCollection)ctx.Cache["quotesCollection"];
            if (quotesCollection == null)
            {
               quotesCollection = new QuotesCollection();
               String strAppPath = Server.MapPath("");
      
               String strFilePathXml =
                  strAppPath + "\\app_data\\QuotesCollection.xml";
               String strFilePathSchema =
                  strAppPath + "\\app_data\\QuotesCollection.xsd";
               quotesCollection.ReadXmlSchema(strFilePathSchema);
               quotesCollection.ReadXml(strFilePathXml);
      
               CacheDependency cacheDependency =
                  new CacheDependency(strFilePathXml);
      
               ctx.Cache.Insert("quotesCollection",
                        quotesCollection,
                        cacheDependency,
                        Cache.NoAbsoluteExpiration,
                        Cache.NoSlidingExpiration,
                        CacheItemPriority.Default,
                        null);
            }
            return quotesCollection;
         }
      
          public QuoteService () {
          }
          [WebMethod]
          public string HelloWorld() {
              return "Hello World";
          }
      
         [WebMethod]
         public Quote GetAQuote()
         {
            QuotesCollection quotesCollection = this.LoadQuotes();
            int nNumQuotes = quotesCollection.Rows.Count;
            Random random = new Random();
            int nQuote = random.Next(nNumQuotes);
            DataRow dataRow = quotesCollection.Rows[nQuote];
            Quote quote = new Quote((String)dataRow["Quote"],
                              (String)dataRow["OriginatorLastName"],
                              (String)dataRow["OriginatorFirstName"]);
            return quote;
          }
      }
    9. Finally, add two more methods: one to add a quote to the QuotesCollection and another to fetch all the quotes. Name the method for adding quotes AddQuote. AddQuote should take a Quote structure as a parameter and use it to create a new row in the QuotesCollection. AddQuote should reserialize the XML and XSD files.

      GetAllQuotes should load the quotes from the cache, place the QuotesCollection in a DataSet, and return a DataSet. Use a DataSet because it marshals very cleanly back to the client. Be sure to adorn the methods with the [] attribute.

      using System;
      using System.Web;
      using System.Data;
      using System.Web.Services;
      using System.Web.Services.Protocols;
      using System.Web.Caching;
      
      public struct Quote
      {
         public String _strQuote;
         public String _strOriginatorLastName;
         public String _strOriginatorFirstName;
         public Quote(String strQuote,
                      String strOriginatorLastName,
                      String strOriginatorFirstName)
         {
             _strQuote = strQuote;
            _strOriginatorLastName = strOriginatorLastName;
            _strOriginatorFirstName = strOriginatorFirstName;
         }
      }
      
      [WebService(Namespace = "http://tempuri.org/")]
      [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
      public class QuoteService : System.Web.Services.WebService
      {
      
         QuotesCollection LoadQuotes()
         {
            QuotesCollection quotesCollection;
      
            HttpContext ctx = HttpContext.Current;
            quotesCollection = (QuotesCollection)ctx.Cache["quotesCollection"];
            if (quotesCollection == null)
            {
               quotesCollection = new QuotesCollection();
               String strAppPath = Server.MapPath("");
      
               String strFilePathXml =
                  strAppPath + "\\app_data\\QuotesCollection.xml";
               String strFilePathSchema =
                  strAppPath + "\\app_data\\QuotesCollection.xsd";
      
               quotesCollection.ReadXmlSchema(strFilePathSchema);
               quotesCollection.ReadXml(strFilePathXml);
      
               CacheDependency cacheDependency =
                  new CacheDependency(strFilePathXml);
      
               ctx.Cache.Insert("quotesCollection",
                        quotesCollection,
                        cacheDependency,
                        Cache.NoAbsoluteExpiration,
                        Cache.NoSlidingExpiration,
                        CacheItemPriority.Default,
                        null);
            }
            return quotesCollection;
          }
      
          public QuoteService () {
      
          }
      
          [WebMethod]
          public string HelloWorld() {
              return "Hello World";
          }
      
         [WebMethod]
         public Quote GetAQuote()
         {
            QuotesCollection quotesCollection = this.LoadQuotes();
            int nNumQuotes = quotesCollection.Rows.Count;
      
            Random random = new Random();
            int nQuote = random.Next(nNumQuotes);
            DataRow dataRow = quotesCollection.Rows[nQuote];
            Quote quote = new Quote((String)dataRow["Quote"],
                              (String)dataRow["OriginatorLastName"],
                              (String)dataRow["OriginatorFirstName"]);
            return quote;
         }
      [WebMethod]
         public void AddQuote(Quote quote)
         {
            QuotesCollection quotesCollection = this.LoadQuotes();
      
            DataRow dr = quotesCollection.NewRow();
            dr[0] = quote._strQuote;
            dr[1] = quote._strOriginatorLastName;
            dr[2] = quote._strOriginatorFirstName;
            quotesCollection.Rows.Add(dr);
      
            String strAppPath = Server.MapPath("");
            String strFilePathXml =
               strAppPath + "\\app_data\\QuotesCollection.xml";
            String strFilePathSchema =
               strAppPath + "\\app_data\\QuotesCollection.xsd";
      
            quotesCollection.WriteXmlSchema(strFilePathSchema);
            quotesCollection.WriteXml(strFilePathXml);
         }
      
         [WebMethod]
         public DataSet GetAllQuotes()
         {
            QuotesCollection quotesCollection = LoadQuotes();
            DataSet dataSet = new DataSet();
            dataSet.Tables.Add(quotesCollection);
            return dataSet;
         }
      
      }

    You now have a Web service that will deliver quotes to the client upon request. You can surf to the ASMX page and try out the methods if you want to see them work. However, the real power lies in writing clients against the Web service so the client can consume the Web Service programmatically.

    NOTE
    Consuming a Web service is nearly as easy as writing one. The Microsoft .NET Framework and Visual Studio have handy utilities that generate proxies for Web services automatically. Visual Studio is not the only way to consume Web services. May modern applications have ways to consume Web services. Web services are meant to be platform independent, and most modern computing platforms support consuming Web services.

    The following example illustrates consuming the QuoteService via a small command line program.

    Use the QuoteService

    1. Add a new subproject to the solution. Make the new project a console application by selecting the Console Application template. Name the project ConsumeWebService.

      Graphic
    2. Highlight the ConsumeWebService project in the solution explorer and right-click. Select Add Web Reference from the local menu.

      Graphic

      Click on the Web services in this solution link:

      Graphic

      Select the QuoteService link. You should see all the available methods. Click the Add Reference button. Visual Studio will generate a proxy for you.

      Graphic TIP

      Another way to generate a proxy is to surf to the Web service, ask for the WSDL manually, and the run the WSDL code through a utility named WSDL.exe.

      For example, the following query string fetches the WSDL:

      http://localhost/WebServiceORama/QuoteService.ASMX?wsdl

      Save the document sent back by the service and run it through the WSDL command line utility:

      C:\>WSDL quoteservice.wsdl

      This will generate a C Sharp file you may use in your application. (WSDL has command line parameters for generated proxies in VB, too.)

      TIP
      At this point, a number of Web services are already available. To get an idea of what's available, poke around some of the other links in the Add Web Reference dialog box. For example, the Universal Description, Discovery and Integration repository (UDDI) lists a number of services. It's interesting to poke around and see what's there.
    3. The namespace in which the new proxy lives reflects the origin of the Web service. Because this proxy came from a service on this machine, the namespace is localhost. To access types from the new service proxy, you must preface the types with localhost. You could also include the localhost in the using block where the other namespaces are imported into the project.

    4. The name of the QuoteService proxy is (strangely enough) QuoteService. You instantiate it like you would any other class. When you call methods, the proxy will wrap the call in a SOAP envelope and send the request to the destination specified within the proxy. Try making calls to GetAQuote, AddQuote, and GetAllQuotes, as shown below.

      using System;
      using System.Collections.Generic;
      using System.Text;
      using System.Data;
      
      namespace ConsumeWebService
      {
         class Program
         {
      
            static void Main(string[] args)
            {
               localhost.Quote quote;
               localhost.QuoteService quoteService;
      
               quoteService = new localhost.QuoteService();
      
               for(int i = 0; i < 10; i++)
               {
                  quote = quoteService.GetAQuote();
                  System.Console.WriteLine("Quote: " + quote._strQuote);
                  System.Console.WriteLine( "Originator: " +
                                    quote._strOriginatorFirstName + "" +
                                    quote._strOriginatorLastName);
                  System.Console.WriteLine();
               }
            }
         }
      }

      When you run the application, you should see some output like this:

      Graphic

      Remember, the beauty of Web services is that they're not tied to a particular platform. The previous example shows how to consume the QuoteService (which is an ASP.NET application). However, Visual Studio builds proxies for any normal Web service. You could easily have searched around for other sites implementing Web services for which Visual Studio will build you a suitable proxy.

    The big advantage to using Web services is that they expose functionality literally worldwide. Because the Internet is so far reaching, you can call method between a client located in the United States and a service located in some place like Australia.

    One of the downsides involved in making calls over such long distances is the latency. In addition to the expense of context switching, the speed of light is finite. Bits having to travel far distances make for long waits during Web method calls. For that reason, the proxies generated by Visual Studio include an asynchronous calling mechanism replete with completion callbacks.

    If you look at the proxy generated by Visual Studio (Visual Studio includes it in the source code set. You may get to it using the Class View, or you may look for the file in the Web References\localhost subdirectory of the project), you'll see multiple versions of the methods exposed by the Web service. For example, there's a GetAQuote method and a Get[[<img src="images/shy.gif"/>]]AQuoteAsync method. The proxy generated by Visual Studio also includes a number of delegates defined for subscribing to completion events.

    For example, the callback delegate defined for being notified when the GetAQuoteAsync method is finished looks like this:

        void GetAQuoteCompletedEventHandler(object sender,
               GetAQuoteCompletedEventArgs e);

    The event callbacks are set up so that the second argument includes the results of calling the method (in this case, a Quote structure).

    To make an asynchronous method call (and then be notified when it's done), you simply need to invent a callback method that matches the corresponding delegate and attach the callback to the instance of the Web service proxy you're working with.

    Listing 19-1 augments the ConsumeWebService application to call the GetAQuoteAsync method.

    Listing 19-1

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Data;
    
    namespace ConsumeWebService
    {
       class Program
       {
    
          static void Main(string[] args)
          {
             localhost.Quote quote;
             localhost.QuoteService quoteService;
    
             quoteService = new localhost.QuoteService();
    
             for(int i = 0; i < 10; i++)
             {
                quote = quoteService.GetAQuote();
                System.Console.WriteLine("Quote: " + quote._strQuote);
                System.Console.WriteLine( "Originator: " +
                                  quote._strOriginatorFirstName + " " +
                                  quote._strOriginatorLastName);
                System.Console.WriteLine();
             }
    
             System.Console.WriteLine();
             localhost.Quote quoteToAdd =
                new localhost.Quote();
             quoteToAdd._strQuote = "256K RAM should be enough for ANYONE";
             quoteToAdd._strOriginatorLastName = "Gates";
             quoteToAdd._strOriginatorFirstName = "Bill";
    
             quoteService.AddQuote(quoteToAdd);
    
             DataSet dataSetQuotes = quoteService.GetAllQuotes();
    
             DataTable tableQuotes = dataSetQuotes.Tables[0];
             foreach (DataRow dr in tableQuotes.Rows)
             {
                System.Console.WriteLine(dr[0] + " " +
                dr[1] + "  "  + dr[2]);
             }
           System.Console.WriteLine(
              "Press enter to fetch a quote using async");
           System.Console.ReadLine();
           quoteService.GetAQuoteCompleted += OnGetAQuoteCompleted;
           quoteService.GetAQuoteAsync();
           System.Console.WriteLine("Press return to end program ");
           System.Console.ReadLine();
    
    
      }
    
    
          // from generated code  :
          //  public delegate
          //  void GetAQuoteCompletedEventHandler(object sender,
          //     GetAQuoteCompletedEventArgs e);
          public static void OnGetAQuoteCompleted(object sender,
               localhost.GetAQuoteCompletedEventArgs e)
          {
             System.Console.WriteLine();
             System.Console.WriteLine("This is the callback for GetAQuote");
             System.Console.WriteLine("Quote: " + e.Result._strQuote);
             System.Console.WriteLine(e.Result._strOriginatorFirstName +
                " " + e.Result._strOriginatorLastName);
          }
    
        }
     }
    

    After running the asynchronous version, you should see output like this. The callback should display two randomly selected quotes¡ªthe result of calling the GetQuote method twice:

    Graphic Graphic

    The screen shots look a bit odd here because of the order in which the code runs. The program fetches ten quotes synchronously. Then, waits for the enter key to be pressed (that's the ¡°Press return to end program¡± line you see. Remember that the last Web method call is running asynchronously, so we see the result of the asynchronous call even as the main thread is waiting for the Enter keypress.

    The callback mechanism is especially useful for application environments that cannot afford to stall. For example, if a Windows application takes too long to handle a message (the cardinal rule is 1/10 of a second or so), the entire application begins to suffer. Windows applications require a continuously running message pump. Calling a Web method within a Windows application is likely to stall the application for longer than you want. By calling the async versions of the Web methods, the calling thread returns immediately so it can continue doing whatever it's supposed to be doing (for example, running a message loop).

    For completeness, this tutorial includes a Visual Basic version of the QuoteService. It's in a separate project named QuoteServiceVB. The file name is QuoteServiceVB.asmx. Listing 19-2 shows the Visual Basic code for the service. Listing 19-3 shows the Visual Basic code for the QuotesCollection.VB utility class.

    Listing 19-2

    Imports
    System
    Imports System.Data
    Imports System.Configuration
    Imports System.Web
    Imports System.Web.Security
    Imports System.Web.UI
    Imports System.Web.UI.WebControls
    Imports System.Web.UI.WebControls.WebParts
    Imports System.Web.UI.HtmlControls
    Imports System.IO
    
    <Serializable()> _
    Public Class QuotesCollection : Inherits DataTable
    
        Public Sub QuotesCollection()
        End Sub
    
        Public Sub Synthesize()
    
            Me.TableName = "Quotations"
            Dim dr As DataRow
    
            Columns.Add(New _
               DataColumn("Quote", GetType(String)))
            Columns.Add(New _
               DataColumn("OriginatorLastName", GetType(String)))
            Columns.Add(New _
                DataColumn("OriginatorFirstName", GetType(String)))
    
            dr = Me.NewRow()
            dr(0) = _
               "Imagination is more important than knowledge."
            dr(1) = "Einstein"
            dr(2) = "Albert"
            Rows.Add(dr)
    
            dr = Me.NewRow()
            dr(0) = _
               "Assume a virtue, if you have it not"
            dr(1) = "Shakespeare"
            dr(2) = "William"
            Me.Rows.Add(dr)
    
            dr = Me.NewRow()
                  dr(0) = _
               "A banker is a fellow who lends you his " + _
               "umbrella when the sun is shining, but wants " + _
               "it back the minute it begins to rain."
            dr(1) = "Twain"
            dr(2) = "Mark"
            Me.Rows.Add(dr)
    
            dr = Me.NewRow()
            dr(0) = "A man cannot be comfortable without " + _
            "his own approval."
            dr(1) = "Twain"
            dr(2) = "Mark"
            Me.Rows.Add(dr)
    
            dr = Me.NewRow()
            dr(0) = "Beware the young doctor and the " + _
            "old barber"
            dr(1) = "Franklin"
            dr(2) = "Benjamin"
            Me.Rows.Add(dr)
    
            dr = Me.NewRow()
            dr(0) = "Reality is merely an illusion, " + _
            "albeit a very persistent one."
            dr(1) = "Einstein"
            dr(2) = "Albert"
            Me.Rows.Add(dr)
    
            dr = Me.NewRow()
            dr(0) = "Beer has food value, but food has " + _
            "no beer value"
            dr(1) = "Sticker"
            dr(2) = "Bumper"
            Me.Rows.Add(dr)
    
            dr = Me.NewRow()
            dr(0) = "Research is what I'm doing when I " + _
            "don() 't know what I'm doing"
            dr(1) = "Von Braun"
            dr(2) = "Wernher""
            Me.Rows.Add(dr)
    
            dr = Me.NewRow()
            dr(0) = "Whatever is begun in anger ends in shame"
            dr(1) = "Franklin"
            dr(2) = "Benjamim"
            Me.Rows.Add(dr)
    
            dr = Me.NewRow()
            dr(0) = "We think in generalities, but " + _
            "we live in details"
            dr(1) = "Whitehead"
            dr(2) = "Alfred North"
            Me.Rows.Add(dr)
    
            dr = Me.NewRow()
            dr(0) = "Every really new idea looks crazy " + _
            "at first."
            dr(1) = "Whitehead"
            dr(2) = "Alfred North"
            Me.Rows.Add(dr)
    
            dr = Me.NewRow()
            dr(0) = "The illiterate of the 21st century will " + _
            "not be those who cannot read and write, but those " + _
            "who cannot learn, unlearn, and relearn."
            dr(1) = "Whitehead"
            dr(2) = "Alfred North"
            Me.Rows.Add(dr)
    
        End Sub
    End Class

    Listing 19-3

    Imports
    System
    Imports System.Web
    Imports System.Data
    Imports System.Web.Services
    Imports System.Web.Services.Protocols
    Imports System.Web.Caching
    
    Public Structure Quote
    
        Public _strQuote As String
        Public _strOriginatorLastName As String
        Public _strOriginatorFirstName As String
    
        Public Sub New(ByVal strQuote As String, _
                ByVal strOriginatorLastName As String, _
                ByVal strOriginatorFirstName As String)
            _strQuote = strQuote
            _strOriginatorLastName = strOriginatorLastName
            _strOriginatorFirstName = strOriginatorFirstName
        End Sub
    
    End Structure
    
    <WebService(Namespace:="http://tempuri.org/")> _
    <WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
    Public Class QuoteServiceVB
        Inherits System.Web.Services.WebService
    
        Function LoadQuotes() As QuotesCollection
    
            Dim quotesCollection As QuotesCollection
    
            Dim ctx As HttpContext
            ctx = HttpContext.Current
    
            Dim o As Object
            o = ctx.Cache("quotesCollection")
    
            quotesCollection = CType(o, QuotesCollection)
            If quotesCollection Is Nothing Then
                quotesCollection = New QuotesCollection()
                Dim strAppPath As String
    
                strAppPath = Server.MapPath("")
    
                Dim strFilePathXml As String
                strFilePathXml = _
                   strAppPath ' _
                   "\\app_data\\QuotesCollection.xml"
    
                Dim strFilePathSchema As String
                strFilePathSchema = _
                   strAppPath ' _
                   "\\app_data\\QuotesCollection.xsd"
    
                quotesCollection.ReadXmlSchema(strFilePathSchema)
                quotesCollection.ReadXml(strFilePathXml)
    
                Dim cacheDependency As CacheDependency
                cacheDependency = New CacheDependency(strFilePathXml)
    
                ctx.Cache.Insert("quotesCollection", _
                     quotesCollection, _
                     cacheDependency, _
                     Cache.NoAbsoluteExpiration, _
                     Cache.NoSlidingExpiration, _
                     CacheItemPriority.Default, _
                   Nothing)
            End If
            Return quotesCollection
        End Function
    
        Public Sub QuoteService()
    
        End Sub
    
        <WebMethod()> _
        Public Function HelloWorld() As String
    
            Return "Hello World"
        End Function
    
        <WebMethod()> _
        Public Function GetAQuote() As Quote
            Dim quotesCollection As QuotesCollection
            quotesCollection = Me.LoadQuotes()
            Dim nNumQuotes As Integer
            nNumQuotes = quotesCollection.Rows.Count
    
            Dim random As Random
            random = New Random()
    
            Dim nQuote As Integer
    
            nQuote = random.Next(nNumQuotes)
    
            Dim dataRow As DataRow
    
            dataRow = quotesCollection.Rows(nQuote)
    
            Dim quote As Quote
            quote = New Quote(CType(dataRow("Quote"), String), _
                CType(dataRow("OriginatorLastName"), String), _
                CType(dataRow("OriginatorFirstName"), String))
            Return quote
    
        End Function
    
        <WebMethod()> _
        Public Sub AddQuote(ByVal quote As Quote)
    
            Dim quotesCollection As QuotesCollection
            quotesCollection = Me.LoadQuotes()
    
            Dim dr As DataRow
            dr = quotesCollection.NewRow()
            dr(0) = quote._strQuote
            dr(1) = quote._strOriginatorLastName
            dr(2) = quote._strOriginatorFirstName
            quotesCollection.Rows.Add(dr)
            Dim strAppPath As String
            strAppPath = Server.MapPath("")
            Dim strFilePathXml As String
            strFilePathXml = _
               strAppPath + "\\app_data\\QuotesCollection.xml"
            Dim strFilePathSchema As String
            strFilePathSchema = _
               strAppPath + "\\app_data\\QuotesCollection.xsd"
    
            quotesCollection.WriteXmlSchema(strFilePathSchema)
            quotesCollection.WriteXml(strFilePathXml)
        End Sub
    
        <WebMethod()> _
        Public Function GetAllQuotes() As DataSet
            Dim quotesCollection As QuotesCollection
            quotesCollection = LoadQuotes()
            Dim dataSet As DataSet
            dataSet = New DataSet()
            dataSet.Tables.Add(quotesCollection)
            Return dataSet
        End Function
    
    End Class


    So, it's pretty neat that you can call a method from one computer to the other. How is that useful? Web services represent the underpinnings of a whole new model for communicating between enterprises. Here are a couple of examples of how they are useful.

    If you've ever gotten a package delivered to you via the United Parcel Service, you almost invariably need to scrawl your name on the big, brown, bulky tablet handed to you by the guy in the brown shirt. When you sign the tablet, UPS knows that you received the package and they can record that information. Tracking packages in real time is really useful for UPS's business. Recipients always want to know where their packages are at any time, and using this technology helps UPS provide this information to end customers.

    UPS undoubtedly spent a great deal of money on their package tracking system. They developed the technology in the early 1990s¡ªlong before even Al Gore knew what the Internet was. With the advent of a worldwide connected network (the Internet), small and manageable wireless devices to connect to the Internet, and a commonly understood wire format (SOAP), enterprises can develop functionality similar to that used by UPS for a fraction of the cost.

    A second way in which Web services will prove useful is in supply chain management. Back in the 1980s, Electronic Data Interchange (EDI for short) promised to allow companies to order supplies and services automatically with little or no human intervention. The idea was that different companies would subscribe to a data format and would be able to order supplies and services from other enterprises in a much more streamlined way.

    Unfortunately, EDI turned out to be mostly a glorified e-mail system. The formats for data interchange were brittle and easily broken. Furthermore, when the format broke, it took a long time for the companies involved to reach another agreement on a new format.

    Web services promise to help solve the problem of a brittle data interchange mechanism. Through more elaborate orchestration frameworks (like BizTalk from Microsoft), Web services promise to make automatic data interchange between enterprises much more doable and affordable than ever before.

    ASP.NET also implements a number of other features for enhancing Web services. For example, sometimes you want to include some metadata as part of a method call. For example, if you want to ensure only paying clients call your Web methods, you might issue them a token to prove they bought the service. The SOAP specification defines SOAP headers as a way to include such metadata in the method call.

    In addition, it's sometimes useful to install pre- and post processing for Web methods. ASP.NET supports various SOAP extensions. For example, if you wanted to write your own encryption mechanism, you might write a client-side and a service-side extension that encrypts and unencrypts messages interacting with your server.

    Web services represent the next generation of computer connectivity. Instead of relying upon a closed network protocol and wire format, Web services open up the availability of an application to the entire world. Web services are built upon an already existing network using a wire format that many enterprises agree upon for making method calls.

    ASP.NET automates the detailed work necessary to unpack a SOAP request and turn it into a real method call. ASMX files are handlers in the same way as ASPX and ASHX files. ASMX files implement IHttpHandler by parsing the incoming XML, calling a method in the code-behind class, and returning a result. Simply adorning the method with the [WebMethod] attribute inserts the necessary functionality.

    Visual Studio is also useful for consuming Web services. By adding a Web reference to your application, Visual Studio will consult the Web service for the WSDL code and use it to build a proxy. From there you simply instantiate the proxy and call methods. The proxy takes care of preparing the SOAP payload and sending it. The proxies generated by Visual Studio also support asynchronous method invocation so that the main calling thread doesn't block for too long.

    To

    Do This

    Create a Web service

    From an ASP.NET project, select Web Site | Add New Item from the main menu

    Select the Web Service template

    Expose a class method as a Web method

    Apply the [WebMethod] attribute immediately preceding the method signature

    Consume a Web service

    From within Visual Studio, select the project in solution explorer.

    Right-click on project

    Select Add Web Reference

    Surf to the Web service. Visual Studio will automatically ask for the WSDL and build a proxy

    discuss this topic to forum

    relation tutorial

    No relevant information

    Category

      NET (110)

    New

    Hot