Published web services and optional nodes in schema
While the previous posts were pretty general .net stuff, this one is much closer to the BizTalk realm.
When publishing a web service from BizTalk, the definition of the arguments and return type is derived from the schema (this is true also when publishing an orchestration as a web service, as the orchestration port has a schema associated with it).
This highlights a scenario not usually found when creating “standard” web services – optional arguments.
We’ve recently found out (the hard way) that when you are adding a web reference to a web service that has optional elements of types that are not nillable (such as dates, booleans or integers) in the schema defined in the WSDL some special behaviour is triggered in the code generator.
Here’s an example - say you have a scheme with a root node called MyMessage and and two elements – MyRequiredElement and MyOptionalElement.
Now consider that MyOptionalElement is both optional (as you must have guessed) and integer (which cannot be null)
When you expose such a schema as a web service and add a web reference to it from a client, the proxy class generated will have three properties – MyRequiredElement, MyOptionalElement and MyOptionalElementSpecified.
So – if you have a property that looks like this –
public int MyProperty
this.myPropertyField = value;
This one would be generated as well –
public bool MyPropertySpecified
this.myPropertyFieldSpecified = value;
The reason for the third property is to allow .net to distinct between a case where no value specified for MyOptionalElement and when a value was provided which is the default value for this type (for instance – 0 when dealing with integers)
To understand the problem consider a scenario where you're submitting a message that does nto contain an instance of an optional node, and that node is of type int.
When the .net deserialiser takes in that message and tries to deserialise it, it has to give a value to that int property , so it will use the default value for the type – 0 (again - this is because int cannot be null)
Now the server taking in the message has no way of knowing that a value was not really specified by the client, but that a default value was specified by the deserialisation logic.
That’s where the ___Specified property comes into play. It will indicate whether the value was specified in the message or used as a default value.
This is quite all right once you know it’s there, but if you don’t it’s a cause for a major headache.
We've actually encountered this from the other direction as we’re not dealing with the server side, but with the client side (BizTalk being the server side). Not being aware of the __specified logic we’ve created a windows client for our published orchestration and used it to call the web service.
We populated the request object’s properties with values and we're very confused when the message arriving to BizTalk was empty.
Apparently the .net serialisation logic has some black magic in it.
When the serialiser creates the message from the object, it checks, for each property its serialising, for the existence of an equivalent __Specified property.
If such property exists it uses it value (true/false) to decide whether to serialise this property or not. If a __Specified property does not exist it does not serialise the property.
Since we did not set the __Specified proerpties to true none of our values we’re actually serialised into the message and therefor it arrived to BizTalk empty.
Again – once we found out the cause the solution was quite simple – generally all we needed to do is make sure to set the __Specified properties for each property we used to "true".
Although this makes the code quite “dirty” so we’ve decided to alter the generated proxy code so that the Setter of each “real” property sets the equivalent __Specified property.
I really don’t know why this is not how the code is generated code by default.
So – this can be a major gotcha and another reason we ended up tweaking the generated web service proxies.
Thanks to Ben Gimblett, John Plummer and Xen Lategan for all their help on this one