Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Generated Date Types in WCF and unexpected 'dateSpecified' fields


:P
On this page:

I just ran into a little issue with a Web Service imported through the WCF Service Utility. The issue revolves around dates imported for a service. In the WSDL the layout looks like this:

<xsd:complexType name="NewEnrolment">

  <xsd:sequence>

    <xsd:element minOccurs="0" maxOccurs="1" name="RequestEnrolmentAdvance" type="xsdLocal6:string1" />

    <xsd:element minOccurs="0" maxOccurs="1" name="Type" type="xsdLocal6:string30" />

    <xsd:element minOccurs="0" maxOccurs="1" name="StartDate" type="xsd:date" />

    <xsd:element minOccurs="0" maxOccurs="1" name="ServiceType" type="xsdLocal6:string30" />

    <xsd:element minOccurs="0" maxOccurs="1" name="ServiceProviderEnrolmentReference" type="xsdLocal6:string50" />

    <xsd:element minOccurs="0" maxOccurs="1" name="EndDate" type="xsd:date" />

    <xsd:element minOccurs="0" maxOccurs="1" name="ParentGuardianDateOfBirth" type="xsd:date" />

    <xsd:element minOccurs="0" maxOccurs="1" name="ParentGuardianCRN" type="xsdLocal6:string10" />

    <xsd:element minOccurs="0" maxOccurs="1" name="ChildDateOfBirth" type="xsd:date" />

    <xsd:element minOccurs="0" maxOccurs="1" name="ChildCRN" type="xsdLocal6:string10" />

    <xsd:element minOccurs="0" maxOccurs="1" name="CCBApprovalId" type="xsdLocal6:string15" />

  </xsd:sequence>

</xsd:complexType>

When the type is imported and the .NET class is generated the type ends up having properties of the generated date field plus shadowed properties that with a Specified extension:

ObjectBrowserImport

When I first ran this code and set these date variables I ended up NOT setting these specified fields and the service was complaining that the values were actually missing. That's somewhat unexpected behavior. Once I set the Specified fields to true for each date sent the service calls went through.

FWIW, the 'Specified' fields are stripped from the outgoing SOAP message (they are marked with XmlIgnoreAttribute in the generated code).

This seems an odd design choice. A nullable DateTime property would have been a much better type choice in this situation to specify whether a date is to be included or not. <shrug>

As it is I suppose I can add code to a service wrapper method (this service will be accessed from a non-.NET client anyway so a wrapper is used anyway) that automatically checks for these date values and sets the flag:

public Message_CreateEnrolment_Output NewEnrolment(Message_CreateEnrolment_Input input)

{

    try

    {

        // *** Some fix up code to make it easier to call the service from the client

        if (input.NewEnrolment.ChildDateOfBirth > DateTime.MinValue)

            input.NewEnrolment.ChildDateOfBirthSpecified = true;

        if (input.NewEnrolment.EndDate > DateTime.MinValue)

            input.NewEnrolment.EndDateSpecified = true;

        if (input.NewEnrolment.StartDate > DateTime.MinValue)

            input.NewEnrolment.StartDateSpecified = true;

        if (input.NewEnrolment.ParentGuardianDateOfBirth > DateTime.MinValue)

            input.NewEnrolment.ParentGuardianDateOfBirthSpecified = true;

 

        // *** Create an instance of the Service Proxy

        EnrolmentInterface_Binding_FaCSIAEnrolmentInterfaceClient client =

            this.GetEnrollmentService();

 

        // *** Call the actual CreateEnrolment method on the service

        EnrolmentInterface_CreateEnrolment_Output output = client.CreateEnrolment(input);

 

        return output;

    }

    catch (Exception ex)

    {

        this.SetError(ex.Message);

    }

 

    return null;

}

 

Works but it's ugly that this has to happen for each of the service methods (especially since each method has different message objects - yuk).

Incidentally it's the 0 or 1 min/maxoccurs that trigger this behavior in the WSDL and it's applied apparently to any value type. It doesn't apply to string values since strings can be null.

Posted in WCF  Web Services  

The Voices of Reason


 

David Parslow
December 13, 2007

# re: Generated Date Types in WCF and unexpected 'dateSpecified' fields

The concept of specified properties to do nullable wsdl elements came before .net 2.0 and generics so that was the technique XmlSerializer used. Also it seems that certain applications such as InfoPath don't like the serialization of nullable generics.

From msdn:
If a schema includes an element that is optional (minOccurs = '0'), or if the schema includes a default value, you have two options. One option is to use System.ComponentModel.DefaultValueAttribute to specify the default value, as shown in the following code. Another option is to use a special pattern to create a Boolean field recognized by the XmlSerializer, and to apply the XmlIgnoreAttribute to the field. The pattern is created in the form of propertyNameSpecified. For example, if there is a field named "MyFirstName" you would also create a field named "MyFirstNameSpecified" that instructs the XmlSerializer whether to generate the XML element named "MyFirstName".

(see http://msdn2.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx)

Rick Strahl
December 13, 2007

# re: Generated Date Types in WCF and unexpected 'dateSpecified' fields

@David - thanks for the link.

Doesn't WCF use DataContractSerializer which certainly would be .NET 2.0.

Yes this would need to be handled at the underlying serializer and schema generating layer but it sure would make things cleaner. Even if SvcUtil would generate the attribute the default attribute to True that would help too since you'd only have to set the value to false if the date would in fact be null.

David Fauber
December 16, 2007

# re:

We ran into this with some autogenerated code by the utitlity that you could use to generate xmlserializable classes from schemas (xsd.exe or something like that) , and I think that was about five years ago. Interesting to see how this stuff sticks around.

Diego Mijelshon
December 20, 2007

# re: Generated Date Types in WCF and unexpected 'dateSpecified' fields

We use a different pattern for these cases, which allows you to use a more natural model.
The only drawback is that you have to modify the proxy by hand.

Example:
[XmlIgnore]
public DateTime? Deadline {get; set;}

#region XML Serialization

[XmlIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public bool __DeadlineSpecified
{
    get
    {
        return Deadline.HasValue;
    }
    set
    {
        if (!value)
        {
            Deadline = null;
        }
    }
}

[XmlElement("Deadline")]
[EditorBrowsable(EditorBrowsableState.Never)]
public DateTime __Deadline
{
    get
    {
        return Deadline ?? default(DateTime);
    }
    set
    {
        Deadline = value;
    }
}

#endregion


I plan to publish an article about this and some other XmlSerialization things, but you can use it or write about it if you want (with attribution).

cbr600gyrl
April 08, 2011

# re: Generated Date Types in WCF and unexpected 'dateSpecified' fields

Awesome! I'm not using WCF, but your code example showed me how to use the "Specified" fields and resolved my problem. Thank you!

Andrew Moyer
November 20, 2012

# re: Generated Date Types in WCF and unexpected 'dateSpecified' fields

I solved this problem with reflection... where all of my *Contract classes subclass ValidatableDataContract... because it's a partial class, I made it is own file, then it was as simple as dropping it in each of my web service client projects, refreshing the service reference, and recompiling. This just blew up in my face in the last few days on a number of projects... it must be related to a Windows Update patch that was recently applied, but I haven't been able to isolate it to which one and remove it... so this fix will have to do. No other code changes (to the core project or the generated proxy!) are required with this, thank god! Just make sure to change "MyWebServiceNameSpace" to be the same one used in your Reference.cs file.

Cheers,
Andy

using System.Reflection;
using System.ComponentModel;
 
namespace MyWebServiceNameSpace
{
    public partial class ValidatableDataContract : object, INotifyPropertyChanged
    {
        public ValidatableDataContract()
        {
            this.PropertyChanged += propertyChanged;
        }
 
        private void propertyChanged(object obj, PropertyChangedEventArgs target)
        {
            PropertyInfo prop = obj.GetType().GetProperty(
                string.Format("{0}{1}", target.PropertyName, "Specified"),
                BindingFlags.Public | BindingFlags.Instance);
 
            if (null != prop && prop.CanWrite)
            {
                prop.SetValue(obj, true, null);
            }
        }
    }
}

Carlos Pineda
February 10, 2014

# re: Generated Date Types in WCF and unexpected 'dateSpecified' fields

The proxy at the client side generates "specified" fields for value types (like long, decimal, datetime). For String fields or references to custom classes (Data Contract) the "specified" field is not generated.

If web service is written with Java, the following annotation indicates that field is no required:
@XmlElement(required=false)

If web service is written in C# .NET, the following attribute indicates that field is no required (conceptually, .NET attributes == Java annotations):
[DataMember(IsRequired=false)]

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024