Generated XSD from DataContract Model

Topics: Service Factory Modeling Edition Forum
Feb 19, 2009 at 4:29 PM
Currently have a debate within Team over XSDs that are generated from DataContract Model. The debate has to do with DataContractCollection and DataContract it contains. For example:

I have a DataContract called "Room" that has three Members called  Length, Width, Height
I have a DataContractCollection called "Rooms" that is a collection of DataContract "Room"
I have a DataContract called "House" that has as one of its Members through Aggregation called "Rooms" which is the DataContractCollection "Rooms"

Now when proxy class is generated from service the classes seem intuitively correct.
"House" contains "Rooms" which contains individual "Room"

Proxy.Request oRequest = new Proxy.Request();
oRequest.House = new Proxy.House();
oRequest.House.Rooms = new Proxy.Rooms();
Proxy.Room myRoom = new Proxy.Room();
myRoom.Length = 10;
myRoom.Width = 15;
myRoom.Height = 9;
oRequest.House.Rooms.Add(myRoom);
myRoom = new Proxy.Room();
myRoom.Length = 20;
myRoom.Width = 30;
myRoom.Height = 9;
oRequest.House.Rooms.Add(myRoom);

However when look at XSD that is generated for service:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://Types/2008/06" targetNamespace="http://Types/2008/06" elementFormDefault="qualified">
   <xs:complexType name="House">
      <xs:sequence>
         <xs:element name="Rooms" type="tns:Rooms" nillable="true" minOccurs="0"/>
      </xs:sequence>
   </xs:complexType>
   <xs:element name="House" type="tns:House" nillable="true"/>
   <xs:complexType name="Rooms">
      <xs:sequence>
         <xs:element name="Rooms" type="tns:Room" nillable="true" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
   </xs:complexType>
   <xs:element name="Rooms" type="tns:Rooms" nillable="true"/>
   <xs:complexType name="Room">
      <xs:sequence>
         <xs:element name="Length" type="xs:int" minOccurs="0"/>
         <xs:element name="Width" type="xs:int" minOccurs="0"/>
         <xs:element name="Height" type="xs:int" minOccurs="0"/>
      </xs:sequence>
   </xs:complexType>
   <xs:element name="Room" type="tns:Room" nillable="true"/>
</xs:schema>

The Actual XML that is sent to service (produced from WCF Tracing):
<?xml version="1.0" encoding="UTF-8"?>
<Request xmlns="http://Services/2008/06" xmlns:q1="http://Types/2008/06" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <House xmlns:a="http://Types/2008/06" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <a:Rooms>
         <a:Rooms>
            <a:Length>10</a:Length>
            <a:Width>15</a:Width>
            <a:Height>9</a:Height>
         </a:Rooms>
         <a:Rooms>
            <a:Length>20</a:Length>
            <a:Width>30</a:Width>
            <a:Height>9</a:Height>
         </a:Rooms>
      </a:Rooms>
   </House>
</Request>

As you can see at the XSD/XML level a "House" contains "Rooms" which contains "Rooms" NOT individual "Room".

Members of the Team are arguing that this is very confusing for those that are NOT .Net developers and are NOT using Proxy class.
These users who are consuming using WSDlL and XSDs.
They argue that the Modeling Diagrams do not correlate with generated XSDs. They believe that Rooms should be a collection of Room.

Can you please address this concern.
Thanks!!!
Feb 20, 2009 at 6:56 PM
Did you try using a MessageContract so yoy may wrap the Room class?
However, I would try first with different serializers (XML) and check out the result.
You can also check some links like:
http://msdn.microsoft.com/en-us/magazine/cc163569.aspx
http://blogs.interakting.co.uk/steve/archive/2008/04/28/WCF-Designing-Web-Services-with-Windows-Communication-Foundation.aspx
http://www.netknowledgenow.com/blogs/onmaterialize/archive/2007/01/18/Control-WCF-Serialization-of-Collections-with-IXmlSerializable.aspx

Feb 20, 2009 at 7:16 PM
yes the above example is using a MessageContract with property Is Wrapped set to true.

Please see my additional post "Why Data Contract Collection"
http://www.codeplex.com/servicefactory/Thread/View.aspx?ThreadId=48007

that further provides information about this issue.
Feb 24, 2009 at 5:59 PM
Edited Feb 24, 2009 at 7:43 PM

Mark,

After some investigations about how the code is being generated from the DataContractCollection element, the problems resides in the “ItemName”element of the DataContractCollection attribute in the generated type for this model element. In your case, that value is “Rooms”and should be “Room” because that is the actual name used for each element, in this case the Room type.

So to shed some light in your team debate, the DataContractCollection element is useful for the advantages that you mention in the thread http://www.codeplex.com/servicefactory/Thread/View.aspx?ThreadId=48007 and also using DataContractCollection expose the collection with the CollectionDataContract attribute which does offer a more collection-like interface than an array (in case you want to customize the generated code that is much like the array scenario).

For a detailed explanation of both approaches you can read “Collection Types in Data Contracts” http://msdn.microsoft.com/en-us/library/aa347850.aspx.

 

And now with the fix;

1)      Open the text template file in the WSSF installation path “\TextTemplates\WCF\CS\DataContractCollection.tt”

2)      Go to line 28 and look for “ItemName” attribute.

            3)   Remove all the attribute and its value (from comma to quotes)  , ItemName = "<#= CurrentElement.DataContract.Name #>". 
                        Before:
[WcfSerialization::CollectionDataContract(Namespace = "<#= CurrentElement.Namespace #>", ItemName = "<#= CurrentElement.Name #>")]

      After:  [WcfSerialization::CollectionDataContract(Namespace = "<#= CurrentElement.Namespace #>")]

4)      For source version, rebuild and test. For binary version, close all VS instances and test.

 

The effect of this fix will let the DataContractCollection attribute to use the default value for the ItemName that will be the one set in the DataContract attribute Name value of the item that is the most likely value to set as an item name. Unfortunately there’s no ItemName property on the DataContractCollection element so if you don’t want to use that value, you may need to change the template in some way to get the value you want there.