views:

299

answers:

1

I have a wcf service written in a contract-first fashion. As some of the elements in the schema uses attributes, I had to create a custom serialization for this (using IXmlSerializable). Below is a snippet of the schema, and the classes, as well as the schema from the wsdl output.

My problem is that even though I render the xsd for my IXmlSerializable classes, the schema is not included in the wsdl. They are just referenced with processContents="lax". This seems to be a problem for consumers of my services, as they can not use their wizards to create clients.

Has anyone encountered this problem? Is there anyway to control the wsdl output to avoid this. I experience the same behaviour using both message contracts and data contracts.

The schema for collection and item:

<xs:element name="TelephoneList">
     <xs:complexType>
      <xs:sequence>
       <xs:element ref="Telephone" maxOccurs="unbounded"/>
      </xs:sequence>
     </xs:complexType>
    </xs:element>
    <xs:element name="Telephone">
     <xs:complexType>
      <xs:simpleContent>
       <xs:extension base="xs:string">
        <xs:attribute name="Type">
         <xs:simpleType>
          <xs:restriction base="xs:NMTOKEN">
           <xs:enumeration value="Preferred"/>
           <xs:enumeration value="Office"/>
           <xs:enumeration value="Mobile"/>
           <xs:enumeration value="Home"/>
           <xs:enumeration value="MobilePhoneFromExternalPartner"/>
           <xs:enumeration value="HomePhoneFromExternalPartner"/>
          </xs:restriction>
         </xs:simpleType>
        </xs:attribute>
       </xs:extension>
      </xs:simpleContent>
     </xs:complexType>
      </xs:element>

The collection class:

[CollectionDataContract(ItemName = "Telephone", Name = "TelephoneList", Namespace = Schema.WorkOrderNamespace)]
public class TelephoneSet : SetBase<Telephone>
{    }

The item class:

    [XmlSchemaProvider("GetSchemaFile")]
    public class Telephone : CustomSerializedEntity //The base class implements IXmlSerializable
    {
        public virtual TelephoneType? Type { get; set; }
        public virtual string Number { get; set; }

        /// <remarks>This method is referenced in the <seealso cref="XmlSchemaProviderAttribute"/> decoration.</remarks>
        public static XmlSchemaComplexType GetSchemaFile(XmlSchemaSet xs)
        {
            return CreateSchema(xs, "Telephone");
        }

        public override void ReadXml(XmlReader reader)
        {
            Type = ReadEnumAttribute<TelephoneType?>(reader, "Type");
            reader.MoveToElement();
            Number = reader.Value;
        }

        public override void WriteXml(XmlWriter writer)
        {
            WriteAttribute(writer, "Type", Type);
            if (!string.IsNullOrEmpty(Number))
            {
                writer.WriteValue(Number);
            }
        }
    }

The xsd rendered by the service:

<xs:complexType name="TelephoneList">
      <xs:sequence>
         <xs:element minOccurs="0" maxOccurs="unbounded" name="Telephone" nillable="true">
            <xs:complexType>
               <xs:sequence>
                  <xs:any minOccurs="0" processContents="lax"/>
               </xs:sequence>
            </xs:complexType>
         </xs:element>
      </xs:sequence>
   </xs:complexType>
   <xs:element name="TelephoneList" nillable="true" type="tns:TelephoneList"/>
A: 

You're returning an XmlSchemaComplexType from your GetSchemaFile method, but the contract of the method for XmlSchemaProviderAttribute dictates that you must return XmlQualifiedName. From the remarks section of the MSDN documentation for XmlSchemaProviderAttribute:

The MethodName property returns the name of a static method through reflection. The method, which must be implemented, must take a single parameter, an XmlSchemaSet object, which the method populates with an XmlSchema object. The method must also return an XmlQualifiedName object that identifies the data type.

The reason for this is simple: imagine you had several types within your schema, how would it know which one the class it's reflecting represents? So by returning an XmlQualifiedName you're actually saying this class maps to this type in this schema.

Drew Marsh
Thanks. Wonder how I missed that! Unfortunately, when returning qualified names, the metadata endpoint fails in a bad request 400 error...
Øyvind Skaar