Recently I encountered a serious problem with BizTalk generated web service while doing Xml validation that uses mandatory elements with enumeration.
- Schemas are exposed as web services using “BizTalk Web Publishing Wizard”.
- The messages come into BizTalk via Web Service and SOAP adapter.
- A custom XML schema validation pipeline component was build, which is capable of returning all the validation errors (the default out of the box BizTalk one only returns the first validation error) in a serializable error collection.
- Published messages are picked up by our business process Orchestration, whose first job is to validate it against the schema using the custom pipeline component created in step #3 (by using XLANGPipelineManager.ExecuteReceivePipeline inside an expression shape and capturing exception). If any validation errors are identified, then they are mapped to response message with appropriate error codes.
Our system heavily depends on the schema validation and different error codes it generates. For example, say if a mandatory element called “channel” is not supplied then an error code called INVALID_CHANNEL will be returned, the consumer will kick off different work flow based on the critical error codes. So, it’s crucial for us to get the schema validation and correct error codes generation spot on.
In our schemas, some of the critical elements are expressed as enums using “derivation-by-restriction” on xs:string. Example: An element named “channelâ€”(mandatory, min=1) will have enumeration values “Postal”, “Email”, “Fax”.
Sample Valid Message:
Sample Invalid Message (because mandatory “channel” element is missing):
If the invalid message is passed to our orchestration as it’s, everything will work fine. The orchestration will do the schema validation, will detect the missing channel element and generates the appropriate response message with correct error code (INVALID_CHANNEL). But the problem lies in the web service through which the message travels. When the web service receives the above invalid message it identifies the missing “channel” and substitutes the first enumeration value as default for the missing element “channel”. So, by the time the message reaches the orchestration it will look like
You can clearly see the problem here. The orchestration won’t throw any schema validation exception since the mandatory “channel” element is present but with completely random value. This might have serious implication on the down line business systems.
The solution to the problem is quite straight forward. All you need to know is set the “nillable” property of the “channel” element to “true”.
We are using BizTalk Server 2006, and thanks mainly go to .NET Framework 2.0 “Nullable<T>” type. I assume the problem still persist in BizTalk Server 2004 based solution which runs on top of .NET 1.1
Under the hood:
When we donâ€™t specify the “nillable” property, the default value is “false”. This will result in web service code being generated in a normal way as shown below
If you recollect in .NET data types are separated into “value” types and “reference” types. “Reference” types are allocated on the CLR heap and the value types are allocated on the stack. Most of the basic data types like int, char, bool etc fall under “value” type so as Enum and Struct.
A value type cannot be nullable because it has enough capacity to express only the values appropriate for that type; it does not have the additional capacity required to express a value of null.
When the web service deserialize the incoming message it will assign null values to the missing the properties. Since a null value cannot be assigned to the Enum type, by default it puts the first value in the list.
You need to remember, the internal representation of enum’s is integers, by default it will start from “0”. So, from the web service perspective it’s assigning the initial value “0” to the property.
(NOTE: The heading of the post is concentrating on Enum, but you will experience similar problem with basic data types like “int” as well, if the mandatory element of type int is not supplied, after serialization an element with value “0” will automatically be inserted by the web service.)
Effect of assigning nullable = true
When we set the nullable property to true in our schema, the way web service code is generated is slightly different as shown below
Nullable<T> Generic structure is new in .NET 2.0 to accommodate the usage of null on value types. See this link for more information http://msdn2.microsoft.com/en-us/library/b3h38hb0.aspx
Now the web service serialization won’t assing the default value to the “channel” element. It will be null instead, so when the message gets serialized it, the “channel” element wont be there.
During this exercise I also identified a strange behaviour, which is not acceptable on an enterprise system. If the serialization fails for some reason, say for example you put a wrong value for the enum, Example <channel>Pigeon</channel> or even put wrong case for example <channel>postal</channel> (‘p’ is lower case). There won’t be any errors or warning raised by the web service and message wonâ€™t be submitted to BizTalk. (NOTE: This is applicable only to one-way web service operations).