An Introduction to Building XML Web Services with ASMX

An Introduction to Building XML Web Services with ASMX

By Thom Robbins (trobbins@microsoft.com)

 

Download PDF Version

 

Download Samples

Overview

The Web Service standards define how to build application functionality that is made available to remote machines using generally available Internet protocols. The Web part defines how application functionality is accessed. The services relate to the idea of providing reusable application functionality without having to download or install application code. Using Web Services, Service Oriented Applications (SOA) capitalize on this set of principles as the way to build and deploy a set of distributed services. Any interaction with the set of services is governed by a set of exposed schema, contract and policies. SOA then extends these basic concepts to define services as autonomous, independently deployed and a version managed entity within an application.  

 

The end result is that Web Services become the general framework for building service endpoints that communicate over HTTP. This enables a design pattern that allows companies to move from the traditional tightly coupled applications to a loosely coupled service oriented model. In this new paradigm larger systems are composed of smaller ones that provide the building blocks to dynamically assemble and deliver cooperative enterprise services. Fundamentally, architecture designed this way allows any application including mobile applications to easily assemble and leverage back end services from existing components.

 

Like SOA, Web Services are an abstract concept based on a growing set of industry driven standards. One of the ways the .NET Framework implements theses is the System.Web.Services namespace within a Web Service end point (.asmx page) that is executed by the ASP.NET runtime. Another way is to use the Windows Communication Foundation (WCF) within the .NET Framework 3.0. In this article we will explore how these integrated concepts are used to build ASMX Web Services with Visual Studio 2005 and the .NET Framework.

The Foundations of Web Services

Figure 2-1 The Web Request Pipeline model

 

The ASP.NET runtime is responsible for providing the execution environment for .NET applications that use the System.Web namespace. Within the .NET Framework this namespace supplies the classes and interfaces that enable browser and server based communication. The ASP.NET runtime component consists of a pipeline and a series of HTTP handlers that provide services for inbound message requests.

 

One of the handlers responsible for processing Web Service requests includes a SOAP transport that directs messages to the ASP.NET .asmx end point. Each incoming message request is processed by an instance of the IHTTPHandler interface as shown in Figure 2-1. This interface defines the context that an ASP.NET application implements to synchronously process HTTP Web requests. It’s these HTTP handlers that are used to interact with the low level request and response services provided by IIS.

ASP.NET Fundamentals

Figure 2-2 The Web Service mapping in IIS

 

All properly formatted HTTP requests contain a fully qualified URL. The file extension contained at the end of the URL is used by IIS to route the message request to a set of Internet Server Application Programming Interfaces (ISAPI) extensions. These extensions as shown in Figure 2-2 contain the list of file mappings used by IIS to service a Web request. By default both .aspx and .asmx pages are mapped to the aspnet_isapi.dll.

 

When any properly formatted requested is received IIS passes it directly to the aspnet_isapi.dll that then forwards the request to the ASP.NET worker process (aspnet_wp.exe). This process is responsible for performing the request processing and returning the result back to the aspnet_isapi.dll that returns the results back to IIS. The ASP.NET worker process hands the message over to a chain of managed objects called the HTTP pipeline. This pipeline is activated by creating a new instance of the HTTPRuntime class and then calling the ProcessRequest method. By default, ASP.NET always has a single worker process running that manages all Web applications in a distinct AppDomain. Each AppDomain has its own instance of the HTTPRuntime class which serves as its entry point to the pipeline. To help process the request the HTTPRuntime initializes several internal objects. These helper objects include the cache manager and internal file system monitor that are used to detect changes in the source files for an application.

 

The HTTPRuntime also creates the application context for the request and fills it with any of the HTTP information specific to the request. Within ASP.NET this context is represented by an instance of the HTTPContext class that encapsulates all information about an individual HTTP request. Additionally, the HTTPRuntime creates a text writer.  The text writer represents an instance of the HTTPWriter class and contains the buffer of text programmatically output by code on the page.

 

Once the HTTP runtime is fully initialized it finds an instance of the HTTPApplication class to route the request. The HTTPApplicationFactory class creates and manages a pool of these objects to handle the incoming requests. The HTTPApplication class generically defines the common properties and events for all ASP.NET application objects. By design the HTTPApplication class handles a single request at a time counting on the HTTPApplicationFactory to manage the entire object pool. The HTTPApplication class also serves as the base class for the Global.asax file. Although, an optional class for developers it serves as the base class for all ASP.NET applications. When the request arrives from the HTTPApplicationFactory the HTTPApplication class creates the application state and then fires the events contained in the Global.asax file.

What are HTTP Handlers?

The HTTPApplication object also gives the registered HTTP modules an opportunity to identify the proper handler to service the incoming page request. This is done by dynamically loading the .NET HTTPHandlers based on the incoming page extension and configuration entries found in the Web.config file. Once the proper handler is identified it is then attached to the ASP.NET event chain. The HTTPHandlers serve as the end point that gets called to handle application level request processing.

Figure 2-3 Defining the Web Service Handler

 

HTTP Handlers are fundamentally classes that implement the IHTTPHandler interface. All of the built in HTTPHandlers are declared in the root Web.config file and used to route inbound message requests to a specific HTTP Handler implementation. Each handler is a .NET class designed to manage a specific set of extensions. For example, the .asmx extension for Web Service routes all requests to the System.Web.Services.Protocols.WebServiceHandlerFactory for Web Service as shown in Figure 2-3.  This namespace then manufactures an instance of the Web Service handler to service the request.

Web Service Fundamentals

 

Figure 2-4 The XML Web Services infrastructure.

 

Web Services are a combined set of hardware and software infrastructure that supports both machine and application message based interaction. Logically, the infrastructure needed to support this as shown in Figure 2-4 is based on a known model of discovery to locate deployed Web Services, service descriptions for definition, and standard wire formats to communicate. Machines and applications are only able to interact with a Web Service through the exposed contract. The contract is a WSDL document that describes the allowable XML documents enclosed in a SOAP envelope.

 

WSDL is an XML based syntax that is used to negotiate the details of a Web Service with client.  Essentially, WSDL provides the contract for an external system to understand things like the typing and binding requirements of a service. Typing communicates exactly what types of data structures must be submitted and the binding details exactly how it must be sent. Any received messages that don’t match these requirements are automatically rejected. XML and SOAP are the most common Web Service messages; however, as we will see later they are not the only possible formats.

The Web Service Implementation

The ASP.NET runtime supports the implementation of Web Services through the HTTP addressable endpoint of an .asmx file. This page is designed to automatically create extensible WSDL, dispatch Web methods, serialize and de-serialize parameters, and provide hooks for message interception within your applications.

***Note***

Visual Studio 2005 introduces a Web project model that can use either IIS or the local file system to develop Web applications. This model is an ideal solution only when developing ASP.NET Web Services and Web Pages that are rendered locally.

 

Figure 2-5 Selecting the Web Service Template.

 

 

ASMX Web services are built using Visual Studio 2005 by selecting the Web Service template as shown in Figure 2-5. This Visual Studio template is used to create the boiler plate designers and import the proper .NET namespaces. Web Services are handled by the System.Web.Services namespaces. When a new Web Service project is created this imports the following base namespaces as shown in Table 1.

 

Table 1: Web Service Namespaces

Namespace

Description

System.Web

This namespace enables browser and server communication for the .Net Framework.

System.Web.Services

This namespace enables the creation of XML Web services using ASP.NET.

System.Web.Services.Protocol

This namespace defines the protocol used to transmit data across the wire during the communication between the Web Service client and server.

 

Figure 2-6 Default set of files created for Web Services

 

Once the project is created it contains the list of files needed to begin building the Web Service as shown in Figure 2-6.

The WebService Directive

The Web Service template adds a special @WebService attribute to the .asmx file as shown below.

<%@ WebService Language="vb" CodeBehind="~/App_Code/Service.vb" _ Class="Service" %>

 

This attribute signifies to the ASP.NET runtime and compiler that this class contains system required information about the Web Service implementation. By default, the URL of the Web Service is derived from the file that contains this attribute. This directive contains the attributes shown in Table 2.

Table2: The attributes of the WebService directive.

Attribute

Description

Class

Specifies the class that implements the Web Service. This is automatically compiled the first time the service is accessed.

CodeBehind

Specifies the source code file that implements the Web Service.

Debug

Specifies whether the Web Service should be compiled with Debug symbols.

Language

Specifies the .NET development language used when compiling inline code.

 

***Note***

The WebService attribute is similar to the @Page directive for .aspx pages.

The WebServiceAttribute Class

The Common Language Runtime (CLR) allows the addition of keyword declarations called attributes to annotate various programming elements like types, fields, methods and properties. These special attributes are saved as part of the application metadata and used to describe special properties of the application code to the runtime. For example these attributes are used to describe how to serialize data, specify characteristics that are used to enforce security and even limit just in time (JIT) compiler optimizations.

 

When code is compiled, it is converted into the Microsoft Intermediate Language (MSIL) and placed into a portable executable (PE) file along with metadata generated by the compiler. The compiler will automatically create these attributes when you declare any instances of a class that is inherited from the System.Attribute like the System.WebServicesAttribute. Attributes are used to place extra descriptive information into the metadata that can be extracted by the runtime reflection services. By default the initial signature for any Web Service in the code behind file contains the template generated code in Listing 2-1 that implements several of these special attributes.

Listing 2-1

<WebService(Namespace:="https://tempuri.org/")> _

<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _

<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _

Public Class Service

    Inherits System.Web.Services.WebService

 

    <WebMethod()> _

    Public Function HelloWorld() As String

        Return "Hello World"

    End Function

End Class

 

Figure 2-7 The auto-generated help page.

 

 

By default, the WebServices Attribute accepts properties as shown in Table 3.Once these attributes are added they become reflected in the system generated help page as shown in Figure 2-7.

Table 3: The Properties of the WebService Attribute

Name

Description

Description

A descriptive message for the Web service.

Name

Gets or sets the name of the Web service.

Namespace

Gets or sets the default namespace to use for the XML Web service.

 

WebService attributes are used to identify or provide additional descriptive information about deployed Web Services. For example, the namespace attribute is used to uniquely identify a Web Service endpoint. By default, when a Web Service project is created by Visual Studio this is set to https://tempuri.org. This is a temporary system defined namespace that is used to identify all Web Services code generated by the .NET framework. Prior to deployment it is important this is changed to a unique namespace that is owned by your organization. For example, you could use the company’s Internet domain name as part of the unique XML namespace.

 

Although many XML Web Services namespaces look like URL’s, they aren’t actually required to point to a valid physical resource on the Web. XML namespaces offer a way to create names in an XML document that are identified by a Uniform Resource Identifier (URI). Within the WSDL of a Web Service, the namespace is used as the default for XML elements. For example, the name of a Web Service and its methods pertain to the namespace specified by the namespace property. Any elements specific to WSDL are associated with the https://schemas.xmlsoap.org/wsdl/ namespace. It’s this combination of XML namespace and elements that uniquely identifies a Web Service. For example, a Web Service with all the completed attributes is shown in Listing 2-2.

Listing 2-2

<WebService(Description:="A test Web Service", Name:="The Web Service Test", Namespace:="https://www.thomscontent.com/")> _

<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _

<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _

Public Class Service

    Inherits System.Web.Services.WebService

 

    <WebMethod()> _

    Public Function HelloWorld() As String

        Return "Hello World"

    End Function

 

End Class

 

 

Figure 2-8 The fully attributed page.

 

When the Web Service is run this modifies the default page as shown in Figure 2-8.

Figure 2-9 The updated WSDL Document

 

There are two ways to access the WSDL document from a Web browser. First is by selecting the Services Description link that is available on the framework generated help pages as shown in Figure 2-9. Also, any Web Service can be passed the ‘?WSDL’ parameter to the URL in order to view the document.

The Webmethod Attribute

All Web Services are built using the WebMethods attribute on the beginning of either a sub or procedure. This attribute is used to expose any public methods or sub procedures to Web Service clients though the .asmx end point. The additional properties of this attribute are used to further configure the behavior of the Web service method. The Webmethod attribute takes the parameters as shown in Table 4.

Table 4: WebMethod Properties

Property Name

Description

Description

A descriptive message describing the Web service method.

MessageName

The name used for the Web service method in the data passed to and returned from an Web service method.

CacheDuration

Gets or sets the number of seconds the response should be held in the ASP.NET cache.

TransactionOption

Indicates the level of transaction support a Web Service method supports.

BufferResponse

Gets or sets whether the response for this request is buffered.

The Description and MessageName Properties

Figure 2-10 An overloaded Web Service

Adding a description to your web methods prevents application consumers from having to guess at its function. It is important to include a description for web methods as you would for the service class. This is especially important when a Web Service contains overloaded methods. For example, assume you have a web Service that contains an overloaded customer lookup as shown in Figure 2-10  If you attempt to run this service without providing a description the IE test page generates a runtime exception as shown in Figure 2-11.

Figure 2-11: Failure to provide a description generates the following error.

 

Commenting the procedure is a similar process to what we did earlier with the class definition. Start each method declaration with a WebMethod attribute. Use the Description property to add a description of your web method and MessageName property to change its name as shown in Listing 2-3.

 Listing 2-3

Imports System.Web

Imports System.Web.Services

Imports System.Web.Services.Protocols

 

<WebService(Namespace:="https://tempuri.org/")> _

<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _

Public Class Service

    Inherits System.Web.Services.WebService

 

<WebMethod(Description:="Customer Lookup by Customer ID",  

MessageName:="CustomerIDLookup")> _

Public Function LookupCustomer(ByVal CustomerID As Integer) As String

        Return "Customer Lookup by Customer ID"

    End Function

<WebMethod(Description:="Customer Lookup by Customer Name", messageName:="CustomerNameLookup")> _

Public Function LookupCustomer(ByVal CustomerName As String) As String

        Return "Customer Lookup by Customer Name"

    End Function

End Class

 

Once the MessageName and Description property is added the IE test page changes to reflect these as shown in Figure2-12.

Figure 2-12 The updated test page

 

By default, the WSDL document includes a <porttype> tag for each of the supported access protocols. For Web Services these are the HTTP POST, HTTP GET, and SOAP as shown in Figure 2-13. This tag contains an XML document element for each of the exposed web methods. In addition the name attributes for each of the input and output elements contain the assigned method names.

Figure 2:13 The updated WSDL document

 

The EnableSession Property

By design, ASP.NET Web Services share the same context as ASP.NET applications. This means that Web Services have access to the ASP.NET Application and Session object infrastructure. While an ASP.NET application has only one Application object, it can have multiple session objects, which is used to store data on a per client basis. For Web Services, the state management mechanism is disabled by default and can be enabled by setting the EnableSession property to true. It is important to remember that enabling session management can decrease application performance.

 

The first time the client submits a request the Web server creates a unique HTTPSessionState object. In order to differentiate between each of the client sessions, a unique identifier is assigned to each session object. The client is responsible for presenting this identifier to the server in order to access any client specific data stored in session state. Typically, this identifier is stored in cookies on a client and automatically included as part of the URL string. By default if cookies are enabled on the Web browser, it automatically presents the appropriate cookie along with the HTTP request. However, Web Services are not able to interact with the Web browser directly which makes session management a manual process. The developer becomes responsible for setting the cookie for each server call. For example Listing 2-4 shows how this can be done to track the number of times a Web Service has been called.

Listing 2-4

Public Class Counter

    Inherits WebService

 

    <WebMethod(Description:="Used to record the number of times this service has been accessed", _

        MessageName:="Web Service Usage")> _

    Public Function ServiceUsage() As Integer

 

        ' If the XML Web service has not been accessed, initialize it to 1.

        If Application("WSServiceUsage") Is Nothing Then

            Application("WSServiceUsage") = 1

        Else

            ' Increment the usage count.

            Application("WSServiceUsage") = CInt(Application("WSServiceUsage")) + 1

        End If

 

        ' Return the usage count.

        Return CInt(Application("WSServiceUsage"))

    End Function

End Class

 

The CacheDuration Property

Properly implemented caching in your Web Services increases both scalability and performance. One of the easiest ways to implement caching is with the CacheDuration property of the WebMethod attribute. .NET implements a type of caching called output caching. This is done by storing matching pairs of requests and responses using an in memory hash table for the specified amount of time. During this time any incoming request that matches a request already in cache forces the server to output the cached response. Use the CacheDuration property as shown in Listing 2-5 to set the number of seconds a request/response pair is held in the cache.

Listing 2-5

<WebService(Namespace:="https://tempuri.org/")> _

<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _

<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _

Public Class CacheClass

    Inherits System.Web.Services.WebService

 

    <WebMethod(Description:="Returns the current time", CacheDuration:=60)> _

    Public Function ReturnCurrentTime() As String

        Return My.Computer.Clock.LocalTime.ToLocalTime

    End Function

 

The caching mechanism is often ideal for web methods that involve processor intensive or other expensive types of queries where the results change infrequently. Because the caching system is based on request/response pairs, it uses fewer server resources in situations where a web methods range of expected input parameters is small. If however you have a wide range of expected parameters the cache hash table can grow quickly and consume a great deal of memory. This can be further aggravated if the web method outputs a large amount of return data.

***Note***

In ASP.NET 2.0 the default HTTP test page method is an HTTP-POST. By default, this type of request is not cached. When testing application that utilizes caching make sure that you change the default request to an HTTP-GET.

The TransactionOption Property

A transaction is defined as a unit of work. When units of work are combined and executed together they collectively result in either success or failure of an operation. One of the most common examples of transactions is a credit card process. In this transaction the credit card number is received, authenticated, and then charged. If any of the individual pieces fails to complete, the entire process is rolled back and started over.

 

.NET provides support for MTS or COM+ style transactions through the System.EnterpiseServices namespace. By default transactions are disabled. .NET supports a set of properties for this attribute as shown in Table 5.

 

Table 5: Properties of the TransactionOption

Transaction Type

Description

Disabled

Indicates that the Web Method does not run within the scope of a transaction. When a request is processed, the Web Service method is executed without a transaction.

NotSupported

Indicates that the Web Service method does not run within the scope of a transaction. When a request is processed the Web Service method is executed without a transaction.

Supported

Indicates that the Web Service method does not run within the scope of transactions. When a request is processed, the Web Service is created without a transaction.

Required

Indicates that the XML Web Service method requires a transaction. Since XML Web Service methods participate as the root object in a transaction, a new transaction is created for the calling method.

RequiresNew

Indicates the XML Web Service method requires a new transaction. When a request is processed the XML Web Service is created within a new transaction.

 

When using .NET transactions within a web method it is only able to participate as the root object in the transaction. This means that your web methods may call other transactions but may not itself be called as part of a transaction started by another object. This limitation is due to the stateless nature of the HTTP protocol. As a result, the Required and RequiresNew values for the TransactionOption are the same and both declare a RequiresNew method that start a new transaction. However, the Disabled, NotSupported, and Supported all disable transactions for the web method despite what their names imply.

 

For example, Listing 2-5 shows an example of beginning a new transaction for the credit card service that is called when a funds transfer is initiated.

Listing 2-5

Imports System

Imports System.Web.Services

Imports System.EnterpriseServices

 

Public Class Bank

    Inherits WebService   

   

    <WebMethod(TransactionOption := TransactionOption.RequiresNew)> _  

    Public Sub Transfer(Amount As Long, AcctNumberTo As string, AcctNumberFrom As string)

       

        Dim objBank As New MyCOMObject()

       

        If objBank.GetBalance(AcctNumberFrom) < Amount Then

            ' Explicitly abort the transaction.

            ContextUtil.SetAbort()

        Else

            ' Credit and Debit method explictly vote within

            ' the code for their methods whether to commit or

            ' abort the transaction.

            objBank.Credit(Amount, AcctNumberTo)

            objBank.Debit(Amount, AcctNumberFrom)

        End If

    End Sub

End Class

     

The BufferedResponse Property

The default behavior for a web method is to store a response in a memory buffer until either the buffer is full or the response is complete. This storage process is called serialization. In most scenarios this works fine because buffering results in improved performance by reducing the amount of transmissions to the client. However, if your web method returns a large amount of data or takes a long time to run, you may want to consider disabling buffering by setting the BufferedResponse property to false. This setting causes .NET to send the response back to the client as it is serialized.

 

Typically, the only time you'll want to set BufferResponse to true is when you're returning more than 16K of data. For all practical purposes, there’s no reason you should even change this default property within an ASMX Web Service. However, for a standard .aspx page that is meant for user output. It can make sense to disable this for long running Web pages like a search page. So that the user can start viewing the data before it’s completely returned. However, because Web Services are designed for host to host communications this type of scenario rarely occurs.

The WebServiceBinding Attribute

One of the most important aspects of using Web Services is that they are designed to be cross platform. The standards for Web Services have been evolving over the last years driven by the Web Service Interoperability Organization (WS-I). This is an open industry organization responsible for driving the adoption of Web Services and ensuring they are conformant across platforms. The goal of the WS-I is to promote and support a set of generic protocols for the interoperable exchange of messages between Web Services. Fundamentally, this means that protocols are independent of any action indicated by the message other than those actions necessary for its secure, reliable and efficient delivery. Interoperable means the message is suitable for multiple operating systems and multiple programming languages. The WS-I has developed a basic profile for Web Services. This profile ensures that Web Services are conformant to a set of known requirements. The profile is enforced through a WSDL element called a conformance claim that is placed in the WSDL.

 

Within WSDL a binding is similar to an interface in that it defines a set of concrete operations. Each Web Service method is a member of either the default binding within a webservice attribute or a binding supplied within the WebServiceBindingAttribute applied to the class that implements the Web Service. The WebServiceAttribute takes the properties shown in Table 6.

Table 6: The WebServiceBinding Properties

Name

Description

ConformsTo

Gets or sets the Web Services Interoperability (WSI) specification to which the binding claims to conform.

EmitsConformanceClaims

Gets or sets a value that indicates whether the binding emits conformance claims.

Location

Gets or sets the location where the binding is defined.

Name

Gets or sets the name of the binding.

Namespace

Gets or sets the namespace associated with the binding.

 

Web Services Description Language (WSDL) defines two styles for how an XML Web service method, or operation, can be formatted in a SOAP message: RPC and Document. RPC formatting refers to formatting the operation according to the SOAP specification for using SOAP for RPC; otherwise known as Section 7 of the SOAP specification. RPC formatting states that all parameters are encapsulated within a single XML element named after the XML Web service method and that each XML element within that element represents a parameter named after it. Both RPC and Document style message can be used to communicate with a Web Service using a Remote Procedure Call (RPC). However, because of its construction the Document style message can also participate more easily in a loosely coupled architecture.

Figure 2-14 The EmitConformanceClaims attribute

 

For example, the default Visual Studio Web Service template includes a WS-I basic profile conformance claim. This ensures that the Web Service is a document literal service and meets the various conformance claims that are needed to guarantee interoperability. In order to reflect and enforce this claim in the WSDL set the EmitConformanceClaims attribute as shown in Figure 2-14. Once set a new attribute is added to the generated WSDL documentation for client consumers as shown in Figure 2-15.

Figure 2-15 The WSDL Document element is modified for the conformance claim.

 

The conformance claim is based on a set of properties that ensure the Web Service is interoperable. Not all possible combinations are actually conformant to the basic profile. However, the WSDL.exe which is used to manufacture the WSDL will validate the conformance claim to ensure that the Web Service meets these necessary requirements. For example, if we were to add in an RPC encoding method as shown in Listing 2-6.

Listing 2-6

Public Class ZipCodeService

    Inherits System.Web.Services.WebService

 

    <SoapDocumentMethod(Use:=Description.SoapBindingUse.Encoded), _

     WebMethod()> _

    Public Function GetCityForZipCode(ByVal ZipCode As String) As String

        Dim con As New SqlConnection

        Dim rtnZipCode As String

 

        con.ConnectionString = ConfigurationManager.AppSettings("ZipDb")

 

        Dim sqlcom As New SqlCommand

        With sqlcom

            .Connection = con

            .CommandType = Data.CommandType.StoredProcedure

            .CommandText = "ZipCodeForCity"

            .Parameters.AddWithValue("@zipcode", ZipCode)

            con.Open()

            rtnZipCode = .ExecuteScalar

        End With

 

        Return rtnZipCode

    End Function

 

Figure 2-16 The error generated for a failed WSI conformance claim.

 

 

When the Web Service is run it will fail the conformance claim and Visual Studio will generate the following error as shown in Figure 2-16.

XML Serialization with Web Services

Figure 2-17 The XML Web Service Lifetime.

 

ASMX based Web Services are built on the standard .NET infrastructure and exposed through namespaces. This means that making an XML Web Service call is similar to a regular method call. The main difference is that instead of calling a method that is located in the client application, you generate a request message over a specified transport. In the case of ASMX this is HTTP. The receiving Web Service then processes the information and sends back a result message to the client application. The process of serialization is used to convert an object's public properties and fields to an XML format for storage or transport. De-serialization re-creates the object in its original state based on the XML output. Figure 2-17 shows the process of communication between a client and an XML Web Service.

 

The following describes the sequence of events that occur when an XML Web service is called:

  1. The client creates a new local instance of an XML Web service proxy class.
  2. The client invokes a method on the proxy class.
  3. The client computer serializes the arguments of the XML Web service method into a SOAP message and sends it over the network to the XML Web service.
  4. The called Web Service receives the SOAP message and de-serializes the XML. It creates an instance of the class implementing the XML Web service and invokes the XML Web service method, passing in the de-serialized XML as arguments.
  5. The XML Web service method executes its code, eventually setting the return value and any outbound parameters.
  6. The Web Service infrastructure serializes the return value and outbound parameters into a SOAP message and sends it over the network back to the client.
  7. The client computer receives the SOAP message de-serializes the XML into the return value, and passes that to the instance of the proxy class.
  8. The client receives the return value.

 

The primary purpose of XML based serialization in the .NET Framework is to convert back and forth the incoming and outbound XML documents and streams to a runtime object. The serialization of XML to a common language runtime object is designed to make the object into a format that is easier to process. However, the serialization of objects to XML persists the objects state in an open, standards compliant and platform agnostic way.

 

The .NET Framework supports XML serialization as either XML that supports the W3C XML Schema Definition (XSD) schema or that is conformant to the serialization format defined in section five of the SOAP specification. XML documents converted to objects by the XML Serialization process are by default strongly typed. The data type information is associated with the elements and attributes in an XML document that conforms to an XSD schema. During the serialization process, only the public properties and fields of an object are serialized.  However, type fidelity is not guaranteed. For example, if you have a customer object that exists in a sales namespace. There is no guarantee that it will be serialized into an object of the same type. However, the benefit is that a serialized object can be shipped from one machine to another without requiring the original type is present on the target machine.

 

Serialization and the Dataset

By default the .NET Framework includes two namespaces that are used to serialize and de-serialize classes into an XML format. The System.Runtime.Serialization serializes .NET classes using a binary formatter. The binary formatter uses a .NET-specific format and protocol to optimize size by reducing the number of bytes sent over the wire.  For example using the code in Listing 2-7 we can serialize a dataset using the binary format.

Listing 2-7

Dim ds As New DataSet

Dim da As New SqlClient.SqlDataAdapter("select * from employee", GetConnectionString())

 

da.Fill(ds)

 

Dim bf As New Runtime.Serialization.Formatters.Binary.BinaryFormatter

Dim fs As New IO.FileStream("..\binary.txt", IO.FileMode.CreateNew)

 

ds.RemotingFormat = SerializationFormat.Binary

bf.Serialize(fs, ds)

 

The System.Xml.Serialization namespace is an XML-centric approach. This namespace is designed to serialize any XML simple or complex type represented in an XSD schema definition. This is the default namespace used by Web Services to encode messages. The DataSet.RemotingFormat is used to determine the serialization format for the dataset. For example, using the code in Listing 2-8 we can serialize a dataset using the XML format.

 

Listing 2-8

Dim ds As New DataSet

Dim da As New SqlClient.SqlDataAdapter("select * from employee", GetConnectionString())

 

da.Fill(ds)

 

Dim bf As New Runtime.Serialization.Formatters.Binary.BinaryFormatter

Dim fs As New IO.FileStream("..\xml.txt", IO.FileMode.CreateNew)

 

' the default is to use the XML Formatter

ds.RemotingFormat = SerializationFormat.Xml

 

bf.Serialize(fs, ds)

 

The XMLSerializer class in the System.XML.Serialization namespace controls the schema centric serialization. This class generates custom XMLSerializationReader/XMLSerializationWriter pairs on a per type basis. By default, the XMLSerializer uses a one to one CLR class to XML complex type mapping. Listing 2-9 shows an example of serializing a custom class.

Listing 2-9

Imports System

Imports System.IO

Imports System.Xml.Serialization

Public Class Form1

 

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

 

        Dim serializer As New XmlSerializer(GetType(OrderedItem))

        Dim i As New OrderedItem()

 

        With i

            .ItemName = "umbrella"

            .Description = "The super dry umbrella for all occasions"

            .Quantity = 100

            .UnitPrice = CDec(18.95)

            .Calculate()

        End With

 

        ' Create a FileStream to write with.

        Dim writer As New FileStream("c:\simple.xml", FileMode.Create)

        ' Serialize the object, and close the TextWriter

        serializer.Serialize(writer, i)

        writer.Close()

 

    End Sub

End Class

 

' This is the class that will be serialized.

Public Class OrderedItem

    Public ItemName As String

    Public Description As String

    Public UnitPrice As Decimal

    Public Quantity As Integer

    Public LineTotal As Decimal

 

 

    ' A custom method used to calculate price per item.

    Public Sub Calculate()

        LineTotal = UnitPrice * Quantity

    End Sub

End Class

 

When the code in Listing 2-9 is executed this creates an XML file that contains the serialized XML data as shown in Listing 2-10.

Listing 2-10

<?xml version="1.0"?>

<OrderedItem xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="https://www.w3.org/2001/XMLSchema">

  <ItemName>Widget</ItemName>

  <Description>Regular Widget</Description>

  <UnitPrice>2.3</UnitPrice>

  <Quantity>10</Quantity>

  <LineTotal>23.0</LineTotal>

</OrderedItem>

 

Classes can customize the serialization process by decorating classes with attributes. For example using the System.ComponentModel.DefaultValueAttribute to define a default property value as shown in List 2-11.

Listing 2-11

Public Class PurchaseOrder

    <System.ComponentModel.DefaultValueAttribute ("2006")> _

    Public Year As String

End Class

 

Another option is to use a pattern that creates a Boolean field recognized by the XmlSerializer, and to apply the XmlIgnoreAttribute to the field. The pattern is created in the form of propertyNameSpecified. For example, if there is a field named "MyFirstName" you would also create a field named "MyFirstNameSpecified" that instructs the XmlSerializer whether or not to generate the XML element named "MyFirstName". This is shown in Listing 2-12.

Listing 2-12

Public Class OptionalOrder

    ' This field's value should not be serialized

    ' if it is uninitialized.

    Public FirstOrder As String

 

    ' Use the XmlIgnoreAttribute to ignore the

    ' special field named "FirstOrderSpecified".

    <System.Xml.Serialization.XmlIgnoreAttribute> _

    Public FirstOrderSpecified As Boolean

End Class

Typed vs Untyped Datasets with Web Services

Along with late bound access to values through weakly typed variables. The dataset also provides access to data through a strongly typed metaphor. When strongly typed, dataset tables and columns are accessed using user friendly names and strongly typed variables.

 

A typed dataset is a derived class. This means that is inherits all the methods, events and properties of the dataset. Also, it provides strongly typed methods, events and properties. This means that you can access tables and columns by name, instead of using a collection based method. Aside from the improved readability of the code, a typed dataset allows the code editor to auto-complete code lines. Also, the strongly typed dataset provides access to correctly typed values at compile time. With strongly typed datasets, a type mismatch error is usually caught when the code is compiled rather than at run time.

 

Many applications use Web Services to take advantage of common reusable service for applications. One of these is the ability to look up area codes. If we wanted to create a simple lookup Web Service that does this we could add use code shown in Listing 2-13 to retrieve a dataset that contains the simple location information for a specific Zip Code.

Listing 2-13

<WebMethod()> _

    Public Function GetFullZipInformation(ByVal ZipCode As String) As DataSet

        Dim con As New SqlConnection

        con.ConnectionString = ConfigurationManager.AppSettings("ZipDb")

 

        Dim sqlcom As New SqlCommand

        With sqlcom

            .Connection = con

            .CommandType = Data.CommandType.StoredProcedure

            .CommandText = "ZipCodeForCity"

            .Parameters.AddWithValue("@zipcode", ZipCode)

        End With

 

        Dim adapter As New SqlDataAdapter(sqlcom)

        Dim ds As New DataSet

 

        con.Open()

        adapter.Fill(ds)

        con.Close()

 

        Return ds

 

    End Function

 

Like Listing 2-12 we wanted to return multiple values using a default un-typed dataset we could use the code shown in Listing 2-14.

 

Listing 2-14

<WebMethod()> _

    Public Function UntypedZipCode(ByVal ZipCode As String) As DataSet

        Dim con As New SqlConnection

        con.ConnectionString = ConfigurationManager.AppSettings("ZipDb")

 

        Dim sqlcom As New SqlCommand

        With sqlcom

            .Connection = con

            .CommandType = Data.CommandType.StoredProcedure

            .CommandText = "GetFullZipInfo"

            .Parameters.AddWithValue("@zipcode", ZipCode)

        End With

 

        Dim adapter As New SqlDataAdapter(sqlcom)

        Dim ds As New DataSet

 

        con.Open()

        adapter.Fill(ds)

        con.Close()

 

        Return ds

 

    End Function

 

Figure 2-18 The returned dataset.

 

When this method is executed it returns the data as shown as in Figure 2-18.

Visual Studio 2005 introduces a TableAdapter as a way to create a strongly typed dataset. TableAdapters are designer generated components that provide communication between an application and the database. The TableAdapter connects to a database, executes queries or stored procedures, and either returns a new data table populated with the returned data or fills an existing DataTable with the returned data. TableAdapters can also be used to send updated data from your application back to the database.

 

While TableAdapters are designed using the Dataset Designer, the TableAdapter classes generated are not generated as nested classes of the DataSet. They are located in a separate namespace specific to each dataset. In addition to the standard functionality of a DataAdapter, TableAdapters provide additional typed methods that encapsulate queries that share a common schema with the associated typed DataTable. This allows them to contain multiple queries as long as they return data that conforms to the same schema.

 

They are created using Data Source Configuration Wizard of the dataset. Additionally they can also be created in existing datasets with the TableAdapter Configuration Wizard or by dragging database objects from Server Explorer onto the Dataset Designer. For example to create a typed dataset object for the GetFullZipInfo stored procedures using the TableAdapter wizard use the following steps.

 

.

Figure 2-19 Add the dataset.

 

  1. Add the dataset item as shown in Figure 2-19

 

Figure 2-20 Select the data connection.

 

 

  1. Using the Table Adapter configuration add the database connection as shown in Figure 2-20.

 

Figure 2-21 Select the command type.

 

  1. Use the existing stored procedure as shown in Figure 2-21

 

Figure 2-22 Select the commands.

 

  1. Select the commands to bind to as shown in Figure 2-22.

 

Figure 2-23 Generate the commands.

 

 

  1. Select the commands to generate as shown in Figure 2-23.

 

Figure 2-24 Review the results.

 

 

  1. The final result is the dataset designer as shown in Figure 2-24.

 

The creation of the table adapter also created an XSD schema structure that is used to manage the structure of the XML that is returned. In order to use this we need to create an instance of the Table Adapter as shown below.

 

  Dim Zipadapt As New ZipCodeTableAdapters.GetFullZipInfoTableAdapter

 

From there we can access and return the data using the code shown in Listing 2-15.

 

Listing 2-15

  <WebMethod()> Public Function TypedZipCode(ByVal ZipCode As String) As ZipCode.GetFullZipInfoDataTable

        Return Zipadapt.GetData(ZipCode)

    End Function

 

Figure 2-25 Typed object containing a built in schema.

 

When returned this exposes the schema structure as shown in Figure 2-25.

Summary

Web Services are an important part of both the physical and logical infrastructure of any organization. They allow developers and applications across the organization to reuse existing service interfaces. The reuse also, helps to ensure a consistency of approach, management and security across the organization. Ideally, this helps to abstract business functionality. This makes Web Services become the central hub for retrieving and obtaining business information.