Exposing Existing Code as a Web Service Using the SOAP Toolkit 2.0
Steve Kirk and Priya Dhawan
Microsoft Developer Network
Summary: This article covers data transformations made to expose existing Microsoft Visual Basic 6.0 code as a Web Service using the Microsoft SOAP Toolkit version 2.0. (7 printed pages)
The Microsoft® SOAP Toolkit version 2.0 simplifies the task of exposing and consuming existing code as a Web Service, as documented in the SOAP Toolkit 2.0 documentation. Some of the primary functions performed on the server side are transformations made between data as passed in various data types by the existing code and the XML messages used between the Web Service client and server. Transformation of simple data types is handled automatically while more complex data types require the developer to supply transformation code.
The data transformation issues covered in this article are not the only issues to consider when evaluating the suitability of existing code for exposure as a Web Service. Other considerations include object and state models, return data sizes, how success is indicated, how error information is returned, the security model (including access control, authentication, and encryption), the execution model (synchronous or asynchronous), how the code is to be distributed, and the transaction model (COM+ transactions or declarative transactions). These issues will be covered in upcoming Architectural Topics articles.
It would be quite a project to cover transformation of all of the data types passed by existing code, so some of the most widely used are covered here. An alternative to transformation by SOAP Toolkit code would be to extend existing code with XML interfaces. Transformation techniques for the following data types are covered:
- ADO 2x Command object
- ADO2x Recordset object
- Stream object
- XMLDOM object
The Microsoft ActiveX® Data Objects (ADO) Command object is often exposed by existing code that accesses a database directly. Although the Command object cannot be passed between tiers of an application operating in separate processes, it can be passed within a process. Returning data through Command object output parameters is more efficient than returning data through an ADO recordset for single-row data entities. This makes the ADO Command object useful for returning single-row entity data.
Existing code in the following example returns an ADO Command object containing data as output parameters. Code in a Custom Type Mapper uses the SoapSerializer object to transform the Command object before it is passed to the web service consumer:
With SoapSerializer ' Transform the CommandType .startElement "CommandType" .writeString Cmd.CommandType .endElement ' Transform CommandText .startElement "CommandText" cmdText = Cmd.CommandText cmdText = Left(Cmd.CommandText, Len(cmdText) - 8) cmdText = Right(cmdText, Len(cmdText) - 7) .writeString cmdText .endElement ' Transform the Parameters collection .startElement "Parameters" For i = 0 To oCmd.Parameters.Count - 1 .startElement Right(oCmd.Parameters(i).Name, _ Len(oCmd.Parameters(i).Name) - 1) .startElement "Direction" .writeString oCmd.Parameters(i).Direction .endElement .startElement "Type" .writeString oCmd.Parameters(i).Type .endElement .startElement "Size" .writeString oCmd.Parameters(i).Size .endElement .startElement "Value" .writeString oCmd.Parameters(i).Value .endElement .endElement Next .endElement End With
Note See Example 1 in the BDAdotNetWebServices2.vb sample code (see top of article for download).
Passing data as Command object parameters is an efficient way to pass data. It is also extensible and offers some type checking.
In the following example, XML data submitted to a Web Service by the consumer is transformed to parameters on an ADO Command object that will be passed to existing code:
Dim Cmd As ADODB.Command Dim Param As ADODB.Parameter ' pNode is MSXML2.IXMLDOMNode containing XML submitted by consumer ' Instantiate an ADO Command object Set Cmd = New ADODB.Command With Cmd ' Apply CommandType and CommandText .CommandType = _ CInt(pNode.selectSingleNode("CommandType").nodeTypedValue) .CommandText = pNode.selectSingleNode("CommandText").nodeTypedValue ' Populate the Parameters collection Set nodeParent = pNode.selectSingleNode("Parameters") For i = 0 To nodeParent.childNodes.length - 1 Set nodeParameter = nodeParent.childNodes(i) Set Param = New ADODB.Parameter With Param .Name = "@" + nodeParameter.nodeName .Direction = _ nodeParameter.selectSingleNode("Direction").nodeTypedValue .Type = nodeParameter.selectSingleNode("Type").nodeTypedValue .Size = nodeParameter.selectSingleNode("Size").nodeTypedValue .Value = factory.getMapper(enXSDstring, _ Nothing).Read(nodeParameter.selectSingleNode("Value"), _ bstrEncoding, encodingMode, lFlags) End With .Parameters.Append oParam Next End With
Note See Example 2 in the BDAdotNetWebServices2.vb sample code (see top of article for download).
ADO 2x disconnected recordsets are commonly used to pass data between tiers of a multi-tier application. Data can be single row, sets of rows, or hierarchical rows.
In this example, existing code returns an ADO Recordset object containing hierarchical row data that is transformed to XML before being returned to the consumer:
Dim RS As ADODB.Recordset Dim DataStream As ADODB.Stream Set RS = pvar Set DataStream = New ADODB.Stream ' Save the recordset object as XML into the stream RS.Save DataStream, adPersistXML ' Read and pass the XML string from the Stream to the ' SoapSerialized object pSoapSerializer.writeXML DataStream.ReadText
Note See Example 3 in the BDAdotNetWebServices2.vb sample code (see top of article for download).
In the following example, XML representing hierarchical row data is used to populate an ADO Recordset object to be passed to existing code:
Dim RS As ADODB.Recordset Dim DataStream As ADODB.Stream Set RS = New ADODB.Recordset Set DataStream = New ADODB.Stream ' Read XML into the Stream DataStream.Open DataStream.WriteText pNode.xml DataStream.Position = 0 ' Open the Recordset object from the Stream RS.Open DataStream Set ISoapTypeMapper_read = RS Set DataStream = Nothing
Note See Example 4 in the BDAdotNetWebServices2.vb sample code (see top of article for download).
A stream can provide an efficient way to pass data between native tiers of an application. It is the primary means for reading XML from Microsoft SQL Server™ 2000.
In the following example, existing code returns a stream of XML representing hierarchical row data, which is transformed before being returned to the consumer:
Dim inStream As ADODB.Stream ' pvar contains stream object returned by existing code Set inStream = pvar ' Pass XML data from stream to SOAP Serializer SoapSerializer.writeString inStream.ReadText
Note See Example 5 in the BDAdotNetWebServices2.vb sample code (see top of article for download).
An XMLDOM object is a good way to pass data between native tiers of a multi-tier application. It provides interface extensibility, type checking, and schema validation.
In the following example, existing code returns an XMLDOMDocument object, which is transformed for return to the consumer:
' pvar contains XMLDOMDocument pSoapSerializer.writeXML pvar.xml
Note See Example 6 in the BDAdotNetWebServices2.vb sample code (see top of article for download).
In the following example, XML representing hierarchical row data submitted by the consumer is used to populate an XMLDOM object to be passed to existing code:
Dim Doc As MSXML2.DOMDocument Set Doc = New MSXML2.DOMDocument ' Load XML into the DOM Document Doc.loadXML pNode.selectSingleNode("NewDataSet").xml Set ISoapTypeMapper_read = Doc
Note See Example 7 in the BDAdotNetWebServices2.vb sample code (see top of article for download).
This article and the accompanying examples cover data transformations that enable existing code to be exposed as a Web Service with the SOAP Toolkit 2.0. Some widely used interface objects are covered here.
The performance of these solutions varies and is also affected by the size of the data being passed. Look for comparisons of these implementations in an upcoming article in this series.
Interface is only one of many things to consider when evaluating the suitability of existing code as a Web Service. Other considerations are security (including authorization, authentication, and encryption), transaction model, state model, the way errors and results are returned, and whether the code executes synchronously or asynchronously.