Incorporating a Schema into Your Web Service


Now that we’ve got our pristine schema that details request and response messages for our Web service, what do we do with it? How do we turn schemas into Web methods? Three approaches are available:

  • Use the .NET Framework’s XML Schema Definition tool, xsd.exe, to generate classes that mirror the schema. We can then create a Web service project as usual and add the classes to the project.

  • Write a WSDL file around the schema and use .NET’s wsdl.exe tool to generate the entire Web service code and a client proxy class as well, if required.

  • Use xsd.exe to generate classes as described in the first suggestion, but continue to work in the handler API and create our own serialization solution. We can then continue to validate the messages against our schema as we receive them.

As you’ll soon learn, the serializer’s inability to capture all the subtleties of a schema that we’ve handwritten means that the most accurate way of incorporating our own schema as a blueprint for messages is the third suggestion in the list. However, if a schema is basic enough, the other two methods will be fine.

Using the XML Schema Definition Tool

The XML Schema Definition tool, or xsd.exe, generates .NET classes from schema files and generates schema files from .NET class libraries. To do this, it uses the same serializer as .NET’s Web service framework.

To demonstrate how it works, we’ve prepared a pretty straightforward schema for our album submission service called AlbumService.xsd. You can find it in the code samples for this book. It looks like this:

<?xml version="1.0" encoding="utf-8" ?> <xs:schema targetNamespace="http://www.notashop.com/wscr"    elementFormDefault="qualified"   xmlns:tns="http://www.notashop.com/wscr"      xmlns:xs="http://www.w3.org/2001/XMLSchema">   <!-- Root element and type for request messages -->   <xs:element name="albumSubmission" type="tns:albumSubmission" />   <xs:complexType name="albumSubmission">     <xs:sequence minOccurs="1" maxOccurs="5">       <xs:element name="artist" type="xs:string" />       <xs:element name="album" type="tns:album" />     </xs:sequence>     <xs:attribute name="sender" type="xs:string" use="required" />   </xs:complexType>   <!-- Root element and type for response messages -->   <xs:element name="submissionReceipt" type="tns:submissionReceipt" />   <xs:complexType name="submissionReceipt">     <xs:sequence minOccurs="1" maxOccurs="5">       <xs:element name="message" type="xs:string" />       <xs:element name="albumID">         <xs:simpleType>           <xs:restriction base="xs:string">             <xs:pattern                value="[0-9A-F]{8}(-[0-9A-F]{4}){3}-[0-9A-F]{12}" />           </xs:restriction>         </xs:simpleType>       </xs:element>     </xs:sequence>   </xs:complexType>   <!-- Other Global declarations -->   <xs:attributeGroup name="albumProperties">     <xs:attribute name="noOfTracks"                    type="xs:unsignedByte" use="required" />     <xs:attribute name="runningTime"                    type="xs:duration" use="optional" />   </xs:attributeGroup>   <xs:complexType name="album">     <xs:simpleContent>       <xs:extension base="xs:string">         <xs:attributeGroup ref="tns:albumProperties" />       </xs:extension>     </xs:simpleContent>   </xs:complexType> </xs:schema>

Our goal here is to generate .NET classes that reflect each complex type for inclusion in our Web service. Xsd.exe is command-line only, so open up the Visual Studio .NET command prompt from the Start menu and navigate to where you’ve stored the schema. Now type enter the following command and press Enter:

xsd.exe /classes AlbumService.xsd

If all goes well, you’ll be presented with a new file, AlbumService.cs, containing our new classes. For example, the complex type for our <albumSubmission> element has been translated into the following tagged class:

[System.Xml.Serialization.XmlTypeAttribute(     Namespace="http://www.notashop.com/wscr")] [System.Xml.Serialization.XmlRootAttribute(     Namespace="http://www.notashop.com/wscr", IsNullable=false)] public class albumSubmission {          [System.Xml.Serialization.XmlElementAttribute("artist")]     public string[] artist;          [System.Xml.Serialization.XmlElementAttribute("album")]     public album[] album;          [System.Xml.Serialization.XmlAttributeAttribute()]     public string sender; }

You saw the serialization tags for Web service classes in Chapter 2, and we’ll look at them again in the next section, but for now note how the multiple occurrences of artist and album are accommodated as arrays. This is the standard way of translating elements that can appear more than once in an instance document into a .NET type.

Create a new C# Web service project in Visual Studio .NET called xsdToService at http://localhost/wscr/06. All you need to do is copy the classes in AlbumService.cs over to xsdToService.asmx.cs and write a Web method that makes use of them. For clarity’s sake, we’ve written one here that returns a simple message and GUID to the client for each album and artist submitted:

[WebMethod] public submissionReceipt SubmitAlbum(albumSubmission aSub) {     submissionReceipt sRec = new submissionReceipt();     sRec.albumID=new string[aSub.album.Length];     sRec.message=new string[aSub.album.Length];     for (int i=0; i< aSub.album.Length; i++)     {         sRec.message[i]= aSub.album[i].Value.ToString() + " by " +              aSub.artist[i].ToString() + " received";         sRec.albumID[i]= System.Guid.NewGuid().ToString();     }     return sRec; }

Lo and behold, we have a working Web service. You can test it against the simple client page called xsdToServiceClient.asmx, which is among the sample files.

What the Serializer Doesn’t Do

The real test is whether the schema in the WSDL file for our new service is at least similar to the one we started out with. Granted, if we use the Web method API, we’ll never see the actual format of the SOAP messages because .NET handles them for us, but we designed the schema as the basis for the service. It would be nice to know that it’s preserved to at least some degree. Open a browser window and look at the schema at http:/ /localhost/wscr/06/xsdToService/xsdToService.asmx?wsdl. It’s the same as what you’d get if you were to compile AlbumService.cs and then generate a schema from the resulting DLL file using xsd.exe.

It appears that some detail has been lost, and in an inconsistent fashion. Of the two attributes we defined as “required,” only one—noOfTracks—is still required. The other, sender, no longer has a use attribute, so it reverts to the default of optional. Our attribute group has gone, written out in full where appropriate by .NET, and the GUID pattern we gave to AlbumID has disappeared, leaving that particular element as just a string.

Most noticeably, the sequence of elements in our request type has been changed from

<xs:sequence minOccurs="1" maxOccurs="5">   <xs:element name="artist" type="xs:string" />   <xs:element name="album" type="tns:album" /> </xs:sequence>

to

<s:sequence>   <s:element minOccurs="0" maxOccurs="unbounded" name="artist"      type="s:string" />    <s:element minOccurs="0" maxOccurs="unbounded" name="album"      type="s1:album" />  </s:sequence>

Our instance document now looks like

<aSub sender="string" xmlns="http://www.notashop.com/wscr">   <artist>string</artist>   <artist>string</artist>   <album noOfTracks="unsignedByte" runningTime="duration" />   <album noOfTracks="unsignedByte" runningTime="duration" /> </aSub>

rather than the following, as was the original intention:

<aSub sender="string" xmlns="http://www.notashop.com/wscr">   <artist>string</artist>   <album noOfTracks="unsignedByte" runningTime="duration" />   <artist>string</artist>   <album noOfTracks="unsignedByte" runningTime="duration" /> </aSub>

Programmatically, it’s just as easy to create messages in one style as the other, but it’s easier to understand our original format. Note that the serializer is not perfect from schema to class or vice-versa. Just check that the key parts of the schema are still there. If they aren’t, adjust the tags and types for the classes, as demonstrated in the next section.

Xsd.exe has a small set of options (listed in Table 6-4) that you can use to get more specific results than we did in our demonstration.

Table 6-4: Xsd.exe Options

Options

Description

/l or /language

Specifies which .NET language the classes will be written in. The choices are CS (the default), VB, or JS.

/o or /outputdir

Specifies which directory your class file will appear in.

/c or /classes

Indicates that you want .NET classes reflecting the schema to be generated. Cannot be used at the same time as /d.

/d or /dataset

Indicates that you want to generate .NET classes derived from System.Data.Dataset that reflect the schema.

/e or /element

Specifies which elements in the schema you want to generate classes for. You can use this flag more than once if required.

/n or /namespace

Generates the classes for the schema within the given .NET namespace.

/u or /uri

Indicates that you want to generate classes only against the elements defined within the given namespace URI.

Each option has a short and a long version. Options that have arguments must be separated from their argument by a colon, as shown here:

xsd.exe /l:VB /o:.\vbclasses /classes AlbumSchema.xsd xsd.exe /u:http://www.notashop.com/wscr /e:album AlbumSchema.xsd

If you’re generating a schema from a DLL or EXE file, you can use only /o and a new flag, /t (or /type), which corresponds to /e for schemas. You can use /t to specify which classes you want to generate a schema for. For example:

xsd.exe /t:album AlbumService.dll

Using Wsdl.exe

The results you get using wsdl.exe to generate a Web service from your schema turn out to be truer to the schema than what you achieve using xsd.exe. However, the downside is that you first have to write the rest of your service’s wsdl document before you can use it. Of course, having read Chapter 3, you’re already an expert, so there’s no problem. Don’t forget that if you’re basing a service on a schema, its binding should be doc/literal.

To save you time, we’ve written a WSDL document for the album entry service and saved it as AlbumService.wsdl in the wscr\06 folder of the book’s sample code. You’ve encountered wsdl.exe before, so we won’t waste time looking at the various switches we can use and we’ll go straight to generating a service from our file:

  1. Open a Visual Studio .NET command prompt and navigate to the folder on your machine containing AlbumService.wsdl.

  2. Generate the service classes for the service by calling wsdl.exe /server AlbumService.wsdl at the prompt. Wsdl.exe produces a file called Albumdatabasesubmissionservice.cs to reflect the service’s name in the .wsdl file.

  3. Open Visual Studio .NET and create a new Web service project called wsdlToService at http://localhost/wscr/06/wsdlToService.

  4. Delete all the code from the service’s code view and paste in everything from Albumdatabasesubmissionservice.cs.

When asked to generate server classes based on a service’s WSDL document, wsdl.exe generates an abstract class for the service and an abstract method for each Web method defined within it. Rather than inheriting this abstract base class, you can use a quick hack: delete the keyword abstract wherever it appears and implement each of the Web methods in the class. In our example, we have only one method—SubmitAlbum. It might not surprise you to learn that its implementation is similar to its counterpart that we built using xsd.exe.

[System.Web.Services.WebMethodAttribute()]  [return: System.Xml.Serialization.XmlElementAttribute("message")] public string[] submitAlbum (     [System.Xml.Serialization.XmlElementAttribute("artist")]      string[] artist,      [System.Xml.Serialization.XmlElementAttribute("album")]      album[] album,      [System.Xml.Serialization.XmlAttributeAttribute()]      string sender,      [System.Xml.Serialization.XmlElementAttribute("albumID")]      out string[] albumID) {     albumID=new string[artist.Length];     string[] message=new string[artist.Length];     for (int i=0; i< artist.Length; i++)     {         message[i]= album[i].Value.ToString() + " by " +              artist[i].ToString() + " received";         albumID[i]= System.Guid.NewGuid().ToString();     }     return message; }

Most obvious here is that wsdl.exe didn’t generate classes for the response. Instead of sending back the convenient submissionReceipt object to the client, as in our previous example, we have to use out parameters to return any piece of information beyond the first. The actual implementation is easier to write because we don’t need to encapsulate it inside a submissionReceipt object, but it’s harder to read—the many tags that qualify this method are spelled out in full, making it somewhat unwieldy.

After you make those changes to the service’s code, you build the service and browse over to http://localhost/wscr/06/wsdlToService/wsdlToService.asmx?wsdl. The schema has no extra elements defined in it, as was the case with xsd.exe, although the same changes do occur in our complex type definitions. For the most part, the WSDL remains unchanged except to reflect the name of the class we generated from the original file and the location of the Web service.

It looks like wsdl.exe is the best choice for incorporating a schema written from scratch into a Web service project. It remains truer to the original schema even if it does suffer from the serializer’s inability to mirror class with schema.

Tip

The serializer doesn’t mirror class with schema for quite a few schema constructs, as you’ve seen. We already mentioned that substitution groups cause problems, but so do complex types defined by choice. From class to schema and back, object graphs cause the most problems, but even something as simple as a public property isn’t transitive through the serializer. It becomes a public field on the return trip from XML. Check to see how the Web method layer works with (or against) your code and take appropriate steps.




Programming Microsoft. NET XML Web Services
Programming MicrosoftВ® .NET XML Web Services (Pro-Developer)
ISBN: 0735619123
EAN: 2147483647
Year: 2005
Pages: 172

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net