Hi all
It is pretty common that developers want to assign one message to another inside and orchestration. And as we all know, this must happen inside a “Construct Message” shape.
Inside the “Construct Message” shape, you can have several shapes, but they must all be either a “Transform” shape or a “Message Assignment” shape. The “Transform” shape is used to execute a map, that will generate the message(s) that is/are to be constructed. The “Message Assignment” shape on the other hand uses the expression editor to let you specify how to assign a value to the message(s) that is/are to be constructed.
Often, there is a need to basically copy a message and then change just a couple of values in the copied message. This can’t be done by changing the values of the existing message, as messages in an orchestration are immutable.
Creating the copy is pretty simple; You just assign one message to another like this:
1: OutputMessage = InputMessage;
Now, what many people realize after doing this and something does not work, like routing, correlation or other is, that this assignment only copies the content of InputMessage to OutputMessage. The context is not copied at all. So what you can do is to add another line of code to your “Message Assignment” shape like this:
1: OutputMessage = InputMessage;
2: OutputMessage(*) = InputMessage(*);
This will copy the context of a message from one message to another… but not the entire context, as it turns out.
I was trying out a demo for property demotion, and for this I let my orchestration receive an input message, transform it to an output message and send this out.
So what I did was that I created two schemas:
and
I let Field1 and Field3 be promoted into the same promoted property from a custom property schema, and I let Field2 and Field 4 be promoted into another property in a custom property schema.
and a map that does not map anything – it just created empty fields for Field3 and Field4.
In my “Construct Message” shape, I then added a “Transform” shape to do the transformation and a “Message Assignment” shape that would copy the properties. I then wanted to make sure the output of the send port had the demoted values inside it.
What I expected to happen was this:
- The XMLReceive pipeline would receive the input, promote the two properties and publish the message to the MessageBox.
- The Orchestration would pick it up, perform the transformation, copy the context and send the message out.
- The XMLTransmit pipeline would demote the two values from context (that I had copied from the input message) into Field3 and Field4
This didn’t happen, though. No values were demoted at all. Now, not demoting into Field4 was expected, since demotion doesn’t work for attributes (or does it? See my blog post coming up in a very short time :-) ), but I really expected something to turn up in Field3.
It turns out, that this statement from my code:
1: OutputMessage(*) = InputMessage(*);
does not copy ALL the context, but only the promoted properties that are marked as MessageContextPropertyBase. The ones that are marked as MessageDataPropertyBase (which is the default) do not get copied over. Now, this actually makes sense, all though it baffled me at first, since properties marked as MessageDataPropertyBase are based on values inside the message, so they cannot just be copied to another message type, since BizTalk cannot guarantee that this property exists on this message type and that the element that points to the promoted property actually exists in the message. Well, I guess BizTalk COULD guarantee that, but Microsoft have chosen not to implement that.
Also, distinguished fields, which are also in the context of the message are not copied over – again, this makes sense, since these are tightly bound to the schema they come from (This doesn’t stop the mapping engine of copying them over in maps on receive ports, though, which is really silly – see here).
SO, in order to get ALL the context copied form my input schema to the output schema, I needed three lines of code:
1: OutputMessage(*) = InputMessage(*);
2: OutputMessage(DemotionTest.Property1) = InputMessage(DemotionTest.Property1);
3: OutputMessage(DemotionTest.Property2) = InputMessage(DemotionTest.Property2);
or I could mark the properties as MessageContextPropertyBase.
Yes, indeed… So remember this from now on:
- Assigning one message to another does NOT copy over the context
- Copying over context using M2(*) = M1(*) does NOT copy over custom properties that are marked as the default (MessageDataContextBase)
- In order to get the rest of the properties copied over, do it manually or change the type of the properties, if applicable.
I hope this helps someone…
--
eliasen