Yesterday a co-worker of mine was having issues serializing an auto-generated BizTalk schema into a .NET object. We found an obscure fix that solved the problem.
In Darren’s Professional BizTalk Server 2006 book, he’s a proponent of working with serializable classes (instead of messages) where possible. In our case, my buddy Prashant was doing some mass Oracle table updates using data retrieved from the BizTalk Siebel adapter. Instead of having countless “Oracle Insert” messages, we discussed simply turning the Siebel messages into .NET objects and using a helper class to do one big transactional insert.
So, he took the Siebel adapter schemas, ran them through xsd.exe, and ended up with a nice .NET object representing all the nodes in the schema. However, upon doing the XLANGMessage “RetrieveAs” operation, he got a gnarly error (actual type names removed) stating:
Cannot use XLANGMessage.RetrieveAs to convert message part part with type [SampleNamespace].[TypeName]+QueryEx2Response to type QueryEx2Response.”
…
Exception type: InvalidCastException
Source: Microsoft.XLANGs.Engine
Target Site: System.Object RetrieveAs(System.Type)
…
Unable to generate a temporary class (result=1).
error CS0030:
Cannot convert type ‘Customer_Complaint_Case_BCResultRecord[]’ to
‘Customer_Complaint_Case_BCResultRecord’
error CS0029:
Cannot implicitly convert type
‘Customer_Complaint_Case_BCResultRecord’ to
‘Customer_Complaint_Case_BCResultRecord[]’
Ouch. Well from reading that, clearly there looks like a problem serializing that “BCResultRecord” array. After doing a quick web search, I came across a newsgroup post discussing the same serialization problem we hit. The solution? Add a temporary “attribute” to the unbounded item to force the xsd.exe tool to properly deal with array types. So, before the change, my offending piece of the Siebel-generated XSD looked like this:
<xsd:complexType name="Customer_Complaint_Case_BCResultRecordSet"> <xsd:sequence> <xsd:element minOccurs="0" maxOccurs="unbounded" name="Customer_Complaint_Case_BCResultRecord" type="BizObj:Customer_Complaint_Case_BCResultRecord" /> </xsd:sequence> </xsd:complexType>
When running xsd.exe, the generated type looked like this …
public partial class QueryEx2Response { private Customer_Complaint_Case_BCResultRecord[][] Customer_Complaint_Case_BCResultRecordSetField; [System.Xml.Serialization.XmlArrayItemAttribute (typeof(Customer_Complaint_Case_BCResultRecord), Namespace="http://schemas.microsoft.com/Business_Objects", IsNullable=false)] public Customer_Complaint_Case_BCResultRecord[][] Customer_Complaint_Case_BCResultRecordSet { get { return this.Customer_Complaint_Case_BCResultRecordSetField; } set { this.Customer_Complaint_Case_BCResultRecordSetField = value; } } }
Here’s where the problem was. So, I *temporarily* tweaked the schema to add the temporary attribute …
<xsd:complexType name="Customer_Complaint_Case_BCResultRecordSet"> <xsd:sequence> <xsd:element minOccurs="0" maxOccurs="unbounded" name="Customer_Complaint_Case_BCResultRecord" type="BizObj:Customer_Complaint_Case_BCResultRecord" /> </xsd:sequence> <xsd:attribute name="temp" type="xsd:string" /> </xsd:complexType>
NOW, after re-running xsd.exe, my generated type looked like this …
public partial class QueryEx2Response { private Customer_Complaint_Case_BCResultRecordSet[] Customer_Complaint_Case_BCResultRecordSetField; [System.Xml.Serialization.XmlElementAttribute ("Customer_Complaint_Case_BCResultRecordSet")] public Customer_Complaint_Case_BCResultRecordSet[] Customer_Complaint_Case_BCResultRecordSet { get { return this.Customer_Complaint_Case_BCResultRecordSetField; } set { this.Customer_Complaint_Case_BCResultRecordSetField = value; } } }
You can see how the generated class now recognizes the “BCResultRecordSet” object as an array, vs. using a double-array of type “BCResultRecord.” Also, the metadata about accessor changed from being a XmlArrayItemAttribute to a XmlElementAttribute. Once this change was made, everything worked perfectly.
I was able to successfully switch the schema back to it’s original form (sans “temporary attribute”), and the serialization still worked fine. The key was adding that temporary attribute for the creation of the serializable class only. You don’t need to keep this temporarily attribute in the schema after that.
I suspect that this situation would arise for many of the auto-generated schemas from the BizTalk adapters (Siebel, Oracle, Peoplesoft, SQL Server etc). It’s quite nice to deal with these messages as pure .NET objects, but watch out for tricky serialization issues.
Technorati Tags: BizTalk
Worked like a charm. Thanks!
Richard,
You are a life saver. I’ve been fighting with this one for a while before I finally found your post. Thanks!