Tip

DataSets and Web services don't mix

The Microsoft .NET Framework has enabled developers to quickly build loosely coupled systems via Web services. In addition to Web services, the .NET Framework provides other benefits for developers. Perhaps one of the most important sets of classes in the .NET Framework is ADO.NET. With ADO.NET, developers can significantly work with disconnected data easier than before. It also allows the data to maintain relationships and referential integrity.

So it would seem only natural that a .NET Web service that is working with a database would want to take advantage of the disconnected data features of ADO.NET, and specifically the DataSet class. Many .NET Web service demos have done just that, creating a Web service that returns a DataSet to be consumed by a UI of some sort, also written in .NET, which can easily bind the data to some controls.

To evaluate the overall solution, developers need to understand that Web services are meant to create technology agnostic sets of functionality -- any client, using any technology, can reuse the features contained in that service. For this to be a "good" Web service, it should be usable by other technologies. Let's take a look at the details of a simple Web service that returns a DataSet and see how it measures up.

After creating a simple WebMethod that returns a DataSet, look at the WSDL (Web Services Description Language) generated by the .NET Framework. Remember that WSDL describes the service, including method names,

    Requires Free Membership to View

parameters and return values. This data is used by the .NET Framework WSDL.exe tool, along with other tools in other technologies, to create a proxy class for client to use when accessing the service. A detailed description of the types used as parameters and return values is essential for generating a useful proxy class.

Below is an excerpt from a simple Web service's WSDL that shows the return value of the GetCustomers WebMethod. Notice that GetCustomersResult is actually comprised of two types -- the first is an XML schema and the second is a type . When the method is executed, it will return a schema describing the result, and then the result itself that could be of any type. This is the WSDL equivalent to late binding.

      <s:element name="GetCustomersResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="GetCustomersResult">
              <s:complexType>
                <s:sequence>
                  <s:element ref="s:schema" />
                  <s:any />
                </s:sequence>
              </s:complexType>
            </s:element>
          </s:sequence>
        </s:complexType>
      </s:element>

The first issue is that any tool that attempts to generate a proxy for this service has no way of knowing what to expect. Type checking will have to be done at runtime.

So how does the .NET Framework know when to convert the result into a DataSet for the client proxy? The key is in the actual XML generated from the call. Below is a subset of the XML that is returned when the WebMethod is called with only one row being returned.

<DataSet xmlns="http://tempuri.org/BadWS/Service1">
  <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xs:element name="NewDataSet" msdata:IsDataSet="true">
      <xs:complexType>
        <xs:choice maxOccurs="unbounded">
          <xs:element name="Table1">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="FirstName" type="xs:string" minOccurs="0" />
                <xs:element name="LastName" type="xs:string" minOccurs="0" />
                <xs:element name="Age" type="xs:int" minOccurs="0" />
              </xs:sequence>
            </xs:complexType>
          </xs:element>
        </xs:choice>
      </xs:complexType>
    </xs:element>
  </xs:schema>
  <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" 
    xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
    <NewDataSet xmlns="">
      <Table1 diffgr:id="Table11" msdata:rowOrder="0" 
     diffgr:hasChanges="inserted">
        <FirstName>Paul</FirstName>
        <LastName>Ballard</LastName>
        <Age>32</Age>
      </Table1>
    </NewDataSet>
  </diffgr:diffgram>
</DataSet>

The schema definition has three columns in the table defined with their types, but notice the bolded sections. The specification for the schema includes a Microsoft specific namespace and then an attribute from that namespace called "IsDataSet" set to true -- the incoming data is actually an ADO.NET DataSet.

So then, a Java-based client application that has no concept of an ADO.NET DataSet is left having to work with the data strictly as XML. While this doesn't preclude interoperability altogether, it does make it significantly more difficult when the end result is just a list of customers with three fields.

By foregoing the use of a DataSet and using instead an array of custom types, this Web service can be significantly more useful and interoperable. An excerpt of the WSDL for the Web service created using custom types is below.

      <s:element name="GetCustomers2Response">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="GetCustomers2Result" 
     type="tns:ArrayOfCustomer" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:complexType name="ArrayOfCustomer">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="unbounded" name="Customer" 
      type="tns:Customer" />
        </s:sequence>
      </s:complexType>
      <s:complexType name="Customer">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="1" name="FirstName" 
      type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="LastName" 
      type="s:string" />
          <s:element minOccurs="1" maxOccurs="1" name="Age" 
      type="s:int" />
        </s:sequence>
      </s:complexType>

Notice that it defines the GetCustomers2Result as an array of a complex type with clear definitions for each field. Now even a Java-based tool can generate a useful proxy that will allow the client developer to use our service much more easily.

Paul Ballard is the Editor of TheServerSide.NET Enterprise .NET Community. Paul is an MCSD, MCAD, and MCSE certified consultant with more than 15 years of experience designing and building Windows- and Web-based distributed applications and is currently specializing in Microsoft's .NET technologies as a consultant, speaker, and trainer. Paul is also a volunteer with INETA and the founder of the Central Pennsylvania .NET User Group.


This was first published in January 2005

There are Comments. Add yours.

 
TIP: Want to include a code block in your comment? Use <pre> or <code> tags around the desired text. Ex: <code>insert code</code>

REGISTER or login:

Forgot Password?
By submitting you agree to receive email from TechTarget and its partners. If you reside outside of the United States, you consent to having your personal data transferred to and processed in the United States. Privacy
Sort by: OldestNewest

Forgot Password?

No problem! Submit your e-mail address below. We'll send you an email containing your password.

Your password has been sent to:

Disclaimer: Our Tips Exchange is a forum for you to share technical advice and expertise with your peers and to learn from other enterprise IT professionals. TechTarget provides the infrastructure to facilitate this sharing of information. However, we cannot guarantee the accuracy or validity of the material submitted. You agree that your use of the Ask The Expert services and your reliance on any questions, answers, information or other materials received through this Web site is at your own risk.