Why is an XSD element of type s:date becoming a string when generating a Service Reference?

吃可爱长大的小学妹 提交于 2019-12-21 17:37:28

问题


I'm trying to create a new Service Reference from a WSDL and all of the properties I expect to be DateTime instead of string.

For example, this xsd definition for Contact:

<s:complexType name="Contact">
    <s:sequence>
        <s:element minOccurs="0" maxOccurs="1" name="Address" type="tns:Address" />
        <s:element minOccurs="0" maxOccurs="1" name="Email" type="s:string" />
        ...
        <s:element minOccurs="1" maxOccurs="1" name="BirthDate" type="s:date" />
</s:sequence>

The type of BirthDate is s:date, but the generated type (in Reference.cs) is a string.

internal partial class Contact : object, IExtensibleDataObject, INotifyPropertyChanged
{
    [OptionalField]
    private MembershipMgmtMediator.Address AddressField;

    [OptionalField]
    private string EmailField;

    private string BirthDateField;
}

If i create a web project and add it as a Web Reference instead of a Service Reference, it correctly becomes a DateTime. I assume that has something to do with the way wsdl.exe and svcutil.exe work behind the scenes, but regardless, I'm stuck on trying to figure out how to correctly get Visual Studio to recognize that this property should be a DateTime.


回答1:


There is some good info in these questions: How to generate xs:Date in WCF OperationContract parameter and Best practices for DateTime serialization in .NET 3.5.

As Alex states in his comment to the question, WCF does not support xs:date types. However, it is perhaps more accurate to say that the default DataContractSerializer does not support that type, while the above questions indicate that the XmlSerializer can handle it.

See this link for a DataContractSerializer against XmlSerializer comparison.

If I run:

svcutil http://my_web_site?wsdl /ser:XmlSerializer /d:C:\temp

Then a WSDL fragment like this:

<s:complexType name="Contact">
    <s:sequence>
        <s:element minOccurs="1" maxOccurs="1" name="BirthDate" type="s:date" />
    </s:sequence>
</s:complexType>

Has this class generated:

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://tempuri.org/")]
public partial class Contact
{

    private System.DateTime birthDateField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(DataType="date", Order=0)]
    public System.DateTime BirthDate
    {
        get
        {
            return this.birthDateField;
        }
        set
        {
            this.birthDateField = value;
        }
    }
}

That svcutil invocation produces two files: Service1.cs and output.config. If I include the code file in the project and add the system.serviceModel bits into the configuration file (i.e., web.config or app.config) I can then call the service as normal. For example:

Service1SoapClient client = new Service1SoapClient("Service1Soap");
var contact = client.GetContact();

This approach is not without disadvantages. The Service1.cs file is markedly different if generated without the /ser:XmlSerializer parameter, where you will get additional classes such as WebMethodNameRequest, WebMethodNameRequestBody, WebMethodNameReponse, WebMethodNameReponseBody and so on. If these classes are important in your interactions with the service, my approach may not work for you.

Edit:

In terms of nullable properties, there is some good information in this question: svcutil.exe - Proxy generated not allowing for nullable fields

To get a property nullable in the generated proxy class, the nillable field needs to be set in the WSDL. So something like this:

<s:element minOccurs="0" maxOccurs="1" name="SomeProperty" type="s:date" nillable="true" />

Would generate a property called public System.Nullable<System.DateTime> SomeProperty in the proxy class.

However in your case you can use the SomePropertySpecified property to indicate the presence or absence of the property. These kinds of properties are generated when you have minOccurs="0".

In terms of date formatting I'm not sure. xs:date values are intended to be yyyy-mm-dd with optional timezone information (w3.org). If Oracle is expecting dates in a different format then I wonder how they can be xs:date values at all.

Is there any documentation or other information you can provide regarding the service you are trying to consume?

Edit 2:

I am a little unclear on exactly what "Dates must be in the database format." means in the Oracle docs. If the type is an xs:date then serializing them to the database format would surely mean that it was no longer an xs:date?

Still, there are some things you try in that regard:

  • Force XmlSerializer to serialize DateTime as 'YYYY-MM-DD hh:mm:ss'
  • http://www.codeproject.com/Articles/43237/How-to-Implement-IXmlSerializable-Correctly

You may need to simply experiment sending a few queries to the web service to see just how this date business affects things.

Are you sure those *IsSpecified parameters aren't there? To use my Contact class above as the example, minOccurs=0 on the BirthDate property would give the Contact class an extra property called BirthDateIsSpecified.




回答2:


While this is not a real solution, I think that it may work as a workaround. It is dirty and ugly, and I know that, but it may be better than having String in your code.

Since your own classes (like Address) are properly processed, you could build a simple wrapper around Date class that you would include in your project and schema. The class would have only a Date property or a field and a getter to it.




回答3:


Although I believe nick_w's answer covers the question quite well (and I'm awarding him the bounty), I'm providing the solution I'm going to use in my specific case, where just using XmlSerializer isn't enough. In the end, I think I'm going to go with an extension that converts DateTime objects to string, using a custom format specifier.

public static class SoapUtils
{
    public static string ToOraDate( this DateTime? dt )
    {
        return dt != null ? dt.Value.ToString("dd-MMM-yyyy",
                                              CultureInfo.InvariantCulture) : 
    }
}

// Calling a service
someDate = DateTime.Now;
service.SomeMethod( someDate.ToOraDate() );


来源:https://stackoverflow.com/questions/3145452/why-is-an-xsd-element-of-type-sdate-becoming-a-string-when-generating-a-service

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!