Saturday, February 27, 2010

.Net / Wcf bug: Cannot convert type 'System.DateTime[]' to 'System.DateTime'

@YaronNaveh

A common bug in .Net 2.0 may cause the following exception when compiling a proxy:

Unable to generate a temporary class (result=1).

error CS0030: Cannot convert type 'System.DateTime[]' to 'System.DateTime'

error CS0030: Cannot convert type 'System.DateTime[]' to 'System.DateTime'

error CS0029: Cannot implicitly convert type 'System.DateTime' to 'System.DateTime[]'

Microsoft did not fix this bug for Wcf so it also gives a similar exception:

There was an error in serializing body of message myRequest: 'Unable to generate a temporary class (result=1).
error CS0030: Cannot convert type 'System.DateTime[]' to 'System.DateTime'
error CS0030: Cannot convert type 'System.DateTime[]' to 'System.DateTime'
error CS0029: Cannot implicitly convert type 'System.DateTime' to 'System.DateTime[]'
error CS0029: Cannot implicitly convert type 'System.DateTime' to 'System.DateTime[]'
'.  Please see InnerException for more details.

Is this error limited to DateTime?

Not at all. You may also get any of these:

Cannot convert type 'System.String[]' to 'System.String'

Cannot convert type 'System.Int32[]' to 'System.Int32'

Cannot convert type 'System.Decimal[]' to 'System.Decimal'

Or with your custom types.

What does this error mean?

For you – nothing. This is a .Net/Wcf bug.

When does this happen?

When the following conditions apply:

1. The wsdl (schema) declares a complex type C.
2. C contains a single element with maxOccures>1 (an array).
3. The wsdl declares an element e of type X with maxOccures>1.

Example:

  <xsd:complexType name="C">
    <xsd:sequence>
      <xsd:element maxOccurs="unbounded" minOccurs="0" name="someDate" type="xsd:date"/>    
    </xsd:sequence>
  </xsd:complexType>

...

<xsd:element maxOccurs="unbounded" minOccurs="0" name="e" type="tns:C"/> 

What is the .Net bug?

The proxy will generate something like this:

        [XmlArrayAttribute]
        [XmlArrayItemAttribute("someDate", typeof(DateTime))]
        public DateTime[][] C {
        ...
        }

This line declares an array of type DateTime[][], but also specifies that each item of the array is of type DateTime. Which is clearly an error since the array items are of type DateTime[] (this is a multidimentional array).

This is the reason why the proxy does not compile.

Workaround #1

Manually edit the proxy and change “typeof(DateTime)” to “typeof(DateTime[])”. This workaround is also described here.

Limitation: This change will be overridden every time you regenerate the proxy (update web/service reference).

Workaround #2

We mentioned that the bug only happens when the type C has one child element. Why not add it some friend so it would not be alone? This means we need to manually edit the wsdl. For example:

<xsd:complexType name="C">
    <xsd:sequence>
      <xsd:element maxOccurs="unbounded" minOccurs="0" name="someDate" type="xsd:date"/>          
<xsd:element minOccurs="0" name="dummyFriend" type="xsd:string"/>     
    </xsd:sequence>
  </xsd:complexType>

Now you may say this changes the schema and makes our proxy not valid. But note we have added an optional field only (minOccurs=0) so as long as we do not assign any value to it in our client this change is non-breaking.

Now let’s see how the proxy generated from the updated wsdl looks like:

public partial class C {
        [XmlElementAttribute("dateValue", DataType="date")]
        public DateTime[] dateValue {
            ...
        }

    [XmlElementAttribute]
        public string dummyFriend {
            ...
        }
    }

Now .Net does not generate the multidimensional array in one shot, but declares the mediating type C.  The proxy will in turn use this type as an array:

public C[] e {…}

which gives us the multidimensional effect.

This compiles immediately so we’re free to go.

@YaronNaveh

What's next? get this blog rss updates or register for mail updates!

10 comments:

Anonymous said...

Yaron, I appreciate you very much for you post!
It helps me to fix the issue and saves great deal of time!

Brian Lakstins said...

This bug bit me as well. It was a shame, since I was starting with XML, and after I made my class with xsd.exe, it would not accept the XML used to make it.

wanderer said...

I actually stumbled across a nicer solution to this problem. I found that by moving the cardinality rules onto the sequence element rather than the child, .NET code generation does not collapse the sequences.

Rather than
< xsd:complexType name="C">
< xsd:sequence>
< xsd:element maxOccurs="unbounded" minOccurs="0" name="someDate" type="xsd:date"/>
< /xsd:sequence>
< /xsd:complexType>

Try
< xsd:complexType name="C">
< xsd:sequence maxOccurs="unbounded" minOccurs="0">
< xsd:element name="someDate" type="xsd:date"/>
< /xsd:sequence>
< /xsd:complexType>

James

Yaron Naveh (MVP) said...

@James - Thanks for the tip!

Heiko Scholze said...

Is this problem solved in the actual .NET 4.5?

Yaron Naveh (MVP) said...

not really sure, but if you still get the exception the workaround should be valid

Robert said...

I tried this with the ImageSet[][] problem and set the typeof(ImageSet) to typeof(ImageSet[]) but I got a 400: bad Request exception. I also tried changing ImageSet[][] to ImageSet[] and got the same error.

Robert said...

I tried the first option with ImageSet[][] by changing the Serialization with typeof(ImageSet) to typeof(ImageSet[]) and got a 400: Bad Request exception. I also tried changing ImageSet[][] to ImageSet[] and got the same error.

Yaron Naveh (MVP) said...

Hi Robert

If you were able to send out a soap request you should compare it to the expected soap (e.g. by a working clinet or the service owner sample). Use Fiddler to get your message

jpcesar said...

solved the problem for us, thanks!