ONDotNet.com    
 Published on ONDotNet.com (http://www.ondotnet.com/)
 See this if you're having trouble printing code examples


O'Reilly Book Excerpts: .NET Framework Essentials

Web Services

by Thuan L. Thai, Hoang Lam

Editor's Note: This is the second of four excerpts from the second edition of O'Reilly's .NET Framework Essentials, 2nd Ed. by Thuan L. Thai and Hoang Lam. The complete series of excerpts covers Web services in the context of the .NET framework. This second part covers what a Web service provider is, and illustrates it with a detailed example.

Web Services Provider

In this section, we describe how to develop a Web Service, first from the point of view of service providers and then of the consumers. Web Services providers implement Web Services and advertise them so that the clients can discover and make use of the services. Because Web Services run on top of HTTP, there must be a web server application of some sort on the machine that hosts the Web Services. This web server application can be Microsoft Internet Information Services (IIS), Apache, or any other program that can understand and process the HTTP protocol. In our examples, we use Microsoft IIS, since that is the only web server currently supported by .NET.

Web Service Provider Example

Related Reading

.NET Framework Essentials
By Thuan L. Thai, Hoang Lam

We will be building a Web Service called PubsWS to let consumers get information from the sample Pubs database. All data access will be done through ADO.NET, so read Chapter 5 before attempting the examples.

Creating a Web Service is a three-step process.

  1. Create a new asmx file for the Web Service. This must contain the <% webservice ... %> directive, as well as the class that provides the Web Service implementation. To the Web Service clients, this asmx file is the entry point to your Web Service. You need to put this in a virtual directory that has the executescripts permission turned on.
  2. Inherit from the WebService class of the System.Web.Services namespace. This allows the derived class to access all the normal ASP objects exposed in the WebService base class. From this point, you can use these ASP objects as if you were developing an ASP-based application. It is highly recommended that you specify a namespace for your Web Service before publishing it publicly because the default namespace, http://tempuri.org/, will not uniquely identify your Web Service from other Web Services. To do this, tag the Web Service class with the Namespace attribute, specifying your own namespace.
  3. Tag the public methods with WebMethod attributes to make web methods--public methods of a distributed component that are accessible via the Web. You don't have to tag a method as WebMethod unless you want that method to be published as a web method.

The following C# code demonstrates a simple Web Service that exposes four methods to Internet clients. We emphasize "Internet" because anyone that can access this asmx file on the web server can access these methods, as opposed to your COM component, which can be accessed only by COM clients:

<%@ WebService Language="C#" Class="PubsWS.PubsWS" %>

namespace PubsWS
{
  using System;
  using System.Data;
  using System.Data.OleDb;
  using System.Web;
  using System.Web.Services;

[WebService(Namespace="http://Oreilly/DotNetEssentials/")]
  public class PubsWS : WebService
  {
    private static string m_sConnStr =
"provider=sqloledb;server=(local);database=pubs; Integrated Security=SSPI";

    [WebMethod(Description="Returns a DataSet containing all authors.")]
    public DataSet GetAuthors(  )
    {
      OleDbDataAdapter oDBAdapter;
      DataSet oDS;

      oDBAdapter = new OleDbDataAdapter("select * from authors", 
                                        m_sConnStr);
      oDS = new DataSet(  );
      oDBAdapter.Fill(oDS, "Authors");
      return oDS;
    }

    [WebMethod]
    public DataSet GetAuthor(string sSSN)
    {
      OleDbDataAdapter oDBAdapter;
      DataSet oDS;
 
      oDBAdapter = new OleDbDataAdapter(
                   "select * from authors where au_id ='"
                   + sSSN + "'", m_sConnStr);
      oDS = new DataSet(  );
      oDBAdapter.Fill(oDS, "SelectedAuthor");
      return oDS;
    }
    
    [WebMethod(MessageName="GetBooksByAuthor",
               Description="Find books by author's SSN.")]
    public DataSet GetBooks(string sAuthorSSN) 
    {
      OleDbDataAdapter oDBAdapter;
      DataSet oDS;

      oDBAdapter = new OleDbDataAdapter(
                      "select * from titles inner join titleauthor on " +
                      "titles.title_id=titleauthor.title_id " +
                      "where au_id='" + sAuthorSSN + "'", m_sConnStr);
      oDS = new DataSet(  );
      oDBAdapter.Fill(oDS, "Books");
      oDBAdapter = new OleDbDataAdapter("select * from authors " +
                      "where au_id='" + sAuthorSSN + "'", m_sConnStr);
      oDBAdapter.Fill(oDS, "Author");

      return oDS;
    }

    [WebMethod]
    public DataSet GetBooks(  ) 
    {
      OleDbDataAdapter oDBAdapter;
      DataSet oDS;

      oDBAdapter = new OleDbDataAdapter("select * from titles" ,
                                        m_sConnStr);
      oDS = new DataSet(  );
      oDBAdapter.Fill(oDS, "Books");
      return oDS;
    }
 
  } // end PubsWS
} 

If you are familiar with ASP, you may recognize the usage of the @ symbol in front of keyword WebService. This WebService directive specifies the language of the Web Service so that ASP.NET can compile the Web Service with the correct compiler. This directive also specifies the class that implements the Web Service so it can load the correct class and employ reflection to generate the WSDL for the Web Service.

Because PubsWS also uses ADO.NET's OLE DB provider for its data-access needs, we have to add a reference to System.Data and System.Data.OleDb, in addition to the System, System.Web, and System.Web.Services namespaces.

Class PubsWS inherits from WebService with the colon syntax that should be familiar to C++ or C# developers:

public class PubsWS : WebService

The four methods that are tagged with WebMethod attributes are GetAuthors( ), GetAuthor( ), GetBooks(string), and GetBooks( ). In C#, you can tag public methods with a WebMethod attribute using the []syntax. In VB, you must use <>. For example, in VB, the second method would be declared as:

<WebMethod(  )> Public Function GetAuthor(sSSN As String) As DataSet

By adding [WebMethod] in front of your public method, you make the public method callable from any Internet client. What goes on behind the scenes is that your public method is associated with an attribute, which is implemented as a WebMethodAttribute class. WebMethodAttribute has six properties:

BufferResponse (boolean)
Controls whether or not to buffer the method's response.

CacheDuration
Specifies the length of time in seconds to keep the method response in cache; the default is not to hold the method response in cache (0 seconds).

Description
Provides additional information about a particular web method.

EnableSession (boolean)
Enables or disables session state. If you don't want to use session state for the web method, you could to disable this flag so the web server doesn't have to generate and manage session IDs for each user accessing this web method. It might improve performance. This flag is true by default.

MessageName
Distinguishes web methods with the same names. For example, if you have two different methods called GetBooks (one method retrieves all books while the other method retrieves only books written by a certain author) and you want to publish both of these methods as web methods, the system will have a problem trying to distinguish the two methods since their names are duplicated. You have to use the MessageName property to make sure all service signatures within the WSDL are unique. If the protocol is SOAP, MessageName is mapped to the SOAPAction request header and nested within the soap:Body element. For HTTP GET and HTTP POST, it is the PathInfo portion of the URI (as in http://localhost// PubsWS/PubsWS.asmx/GetBooksByAuthor).

TransactionOption
Can be one of five modes: Disabled, NotSupported, Supported, Required, and RequiresNew. Even though there are five modes, web methods can only participate as the root object in a transaction. This means both Required and RequiresNew result in a new transaction being created for the web method. The Disabled, NotSupported, and Supported settings result in no transaction being used for the web method. The TransactionOption property of a web method is set to Disabled by default.

To set up these properties, pass the property name and its value as a name = value pair:

 [WebMethod(Description="Returns a DataSet containing all authors.")]
 public DataSet GetAuthors(  )

You can separate multiple properties with a comma:

 [WebMethod(MessageName="GetBooksByAuthor",
            Description="Find books by author's SSN.")]
 public DataSet GetBooks(string sAuthorSSN) 

Web.Config

If you set up your Web Services from scratch, you should also need to provide the configuration file (web.config) in the same directory as your asmx file. This configuration file allows you to control various application settings about the virtual directory. Here, we set the authentication mode to None to make our Web Services development and testing a little easier. When you release your Web Services to the public, you should change this setting to Windows, Forms, or Passport instead of None:

<configuration>
  <system.web>
    <authentication mode="None" />
  </system.web>
</configuration>

The following list shows the different modes of authentication:

Forms
Basic Forms authentication is where unauthenticated requests are redirected to a login form.

Windows
Authentication is performed by IIS in one of three ways: basic, digest, or Integrated Windows Authentication.

Passport
Unauthenticated requests to the resource are redirected to Microsoft's centralized authentication service. When authenticated, a token is passed back and used by subsequent requests.

Discover files

After creating the Web Service, you can provide the supporting files to help in the discovery of the service. The static discovery disco file is as follows:

<?xml version="1.0" ?>
<disco:discovery xmlns:disco="http://schemas.xmlsoap.org/disco/" 
                 xmlns:scl="http://schemas.xmlsoap.org/disco/scl/">
<scl:contractRef ref="http://localhost/PubsWS/PubsWS.asmx?WSDL"/> 
</disco:discovery>

Next in this series of excerpts, you'll learn Web Services Consumers using the .NET framework.


View catalog information for .NET Framework Essentials, 2nd Ed.

Return to the .NET DevCenter.

Copyright © 2009 O'Reilly Media, Inc.