BizTalk messages have both a body and a context. Values existing in the context of the message are the equivalent of the things you would write on the envelope of a piece of mail, where the body is what's written on the letter inside. Context property values are used for routing messages to subscriptions, but they can also be used to do neat stuff inside of pipelines, as they are easily accessible.
How do values get placed inside the context of a message? They are either put there by an adapter, a pipeline component, or an orchestration expression. However, the way this happens is tied directly to an often-overlooked setting in BizTalk property schemas: the Property Schema Base.
What is Property Schema Base? The property description in Visual Studio says "This property indicates the BizTalk base class that is used when generating the .NET type for this property. Default value is 'MessageDataPropertyBase'." Sounds like one of those obscure properties you can leave set to the default value, doesn't it? No. Don't even think about skipping over this property without thinking about the consequences, especially because the default is often not what you want.
There are three choices for this property, only two of which you'd really conceivably use, and only one of which makes the most sense the majority of the time. Using MessageDataPropertyBase, the default, means that there must be an element or attribute in the body of the message that has its value promoted to this property in order for the property to appear in the context of the message. MessageContextPropertyBase does not have this restriction - you can write any old value you like into this property's value in the context.
Let's say I've got a schema, MySchema, that has no promoted properties; i.e. I didn't use the Promote > Show Promotions > Property Fields tab in the schema designer. In an orchestration, I create a MySchema message called MyMsg, and I want to assign some context properties using parentheses notation, like so:
MyMsg(MyContextProperties.Prop1);
MyMsg(MyContextProperties.Prop2);
If Prop1 and Prop2 are MessageContextPropertyBase properties, things work out like you would expect. They show up in Intellisense while you are typing, and when the message gets sent out of the orchestration, the properties are promoted.
If Prop1 and Prop2 are MessageDataPropertyBase properties, they don't show up in Intellisense, and you get a red squiggly if you type them in yourself. What's wrong? The values aren't promoted in the schema of your message. If you open up MySchema and promote an element or an attribute to one of your MessageDataPropertyBase properties, rebuild, and try again, the property will show up in Intellisense with a different mini-icon than the other context properties. Actually, there's some funky behavior in Visual Studio where if your property schema is in another project (regardless of whether it's in a referenced DLL or a referenced project in this solution), MessageDataPropertyBase properties won't show up in the initial Intellisense pop-up - you need to type in the .NET namespace first and press "." and then any MessageDataPropertyBase properties will show up.
This difference may seem trivial, but it has important effects on the way that context properties can be used in your workflows. At a recent job, my client had a fairly sophisticated tracking system implemented within BizTalk to feed information about messages into BAM tables, and a web GUI could be used to access and view that tracking information. The system was based on a custom property schema and reusable pipeline components that wrote certain properties to the BAM tables. In order to get "end-to-end tracking," you needed to take the properties from the original message and promote them in your orchestration (if applicable) to the outbound message, so when it passed through the pipeline component on the send side, all the values would be registered with a successful send action.
Unfortunately, when the client had implemented the property schema (which was now a dependency of many, many projects and couldn't be changed without massive headaches), they never changed the default Property Schema Base. This isn't a problem if your outbound message has all of the tracking information as values somewhere in the message body. But what if the output of the orchestration is a message that doesn't have data fields for all that information? You're out of luck, and you have to use the official workaround: At the end of the orchestration, if everything was successful, send the inbound message (with all of its context properties still attached) back out through a pipeline with the send-side tracking component. Of course, you didn't want these messages hanging around, so you shouldn't send it to a folder to a queue. Where was the accepted place to send it? Through the HTTP adapter to an ASP page, installed on the local IIS instance, that does nothing. =)
You can use the Property Promotion dialog to promote field values to properties regardless of whether they are MessageDataPropertyBase or MessageContextPropertyBase properties. So why would you ever want to use MessageDataPropertyBase properties? You would want to use them if it is appropriate for your architecture. MessageContextPropertyBase properties are globally accessible - if someone can reference your property schema, they can use the MessageContextPropertyBase properties in it. In the case of the tracking system above, the properties used for tracking should have been globally accessible. However, let's say I'm creating a workflow with two different orchestrations - I want MoneyTransfer messages with a Value over 1,000,000.00 to go into one orchestration, and the rest of the MoneyTransfer messages to go into the other orchestration. In this case, it doesn't really make sense for the Value data to be in the context of any messages besides MoneyTransfers, and it's always going to be in the body of the inbound message, so it makes the most sense for it to be a MesssageDataPropertyBase property. Note that this doesn't expressly stop others from using it: they could reference your assembly as a property schema and promote values from their schema to your properties. However, it prevents your property from showing up in the global list, where it doesn't really belong.
Saturday, July 26, 2008
Thursday, July 10, 2008
AS2 and 64-bit
The AS2 decode and AS2 disassembly pipeline components cannot run on a 64 bit host. Ensure that they are hosted by a 32 bit host.
Monday, June 2, 2008
Escaping for the SQL adapter
Just came across this funny quirk:
I've been working on a BizTalk/SQL interface through the SQL Send adapter, where the stored procedure being called takes in XML as a parameter (we are using the XML datatype, but I'm pretty sure you could use a char/varchar type as well), and the sproc makes some OPENXML calls to parse the XML and do some insertions.
When you use the Add Adapter Metadata to create the schema that the adapter uses to call the stored procedure, each parameter is represented as an XML attribute. Since at least one of these parameters is a string with XML in it, the string needs to be properly escaped, because <'s are illegal in attribute values.
SQL supports taking in escaped XML, but there's one thing you should know, that I only discovered by trial and error: ampersands need to be doubly-escaped, to "$amp;amp". I'm not sure why this is; my best guess is that when the statement is executed, after characters are de-escaped, the ampersand is an illegal character within SQL. By double-escaping it, the character will be escaped (and subsequently ignored) again during the execution pass.
As far as I've been able to tell, simply using HttpUtility.HtmlEncode will do all the work you need, except for escaping the ampersand twice. Escape all ampersands with a String.Replace BEFORE calling HtmlEncode for the correct result.
Also, note that HtmlEncode does NOT escape single quotes (apos's). Usually, this doesn't lead to problems, even though single quotes are reserved in XML. I'm not up to snuff on whether attribute values are *supposed* to be surrounded with double quotes or single quotes, but I do know that BizTalk will accept messages either way, and the messages that BizTalk generates have double quotes around attribute values. If you have a value wrapped in double quotes, single quotes within that value won't hurt anything, but they will if the value is wrapped in single quotes. Just something to watch out for.
I've been working on a BizTalk/SQL interface through the SQL Send adapter, where the stored procedure being called takes in XML as a parameter (we are using the XML datatype, but I'm pretty sure you could use a char/varchar type as well), and the sproc makes some OPENXML calls to parse the XML and do some insertions.
When you use the Add Adapter Metadata to create the schema that the adapter uses to call the stored procedure, each parameter is represented as an XML attribute. Since at least one of these parameters is a string with XML in it, the string needs to be properly escaped, because <'s are illegal in attribute values.
SQL supports taking in escaped XML, but there's one thing you should know, that I only discovered by trial and error: ampersands need to be doubly-escaped, to "$amp;amp". I'm not sure why this is; my best guess is that when the statement is executed, after characters are de-escaped, the ampersand is an illegal character within SQL. By double-escaping it, the character will be escaped (and subsequently ignored) again during the execution pass.
As far as I've been able to tell, simply using HttpUtility.HtmlEncode will do all the work you need, except for escaping the ampersand twice. Escape all ampersands with a String.Replace BEFORE calling HtmlEncode for the correct result.
Also, note that HtmlEncode does NOT escape single quotes (apos's). Usually, this doesn't lead to problems, even though single quotes are reserved in XML. I'm not up to snuff on whether attribute values are *supposed* to be surrounded with double quotes or single quotes, but I do know that BizTalk will accept messages either way, and the messages that BizTalk generates have double quotes around attribute values. If you have a value wrapped in double quotes, single quotes within that value won't hurt anything, but they will if the value is wrapped in single quotes. Just something to watch out for.
Monday, March 24, 2008
SMTP.EmailBodyTextCharset
Jotting this one down really quick...
If you send a plain text email by assigning the SMTP.EmailBodyText context property on a message (keep it short... the property limit is 255 chars!), you MUST set EmailBodyTextCharset on that message, or you will receive a very unhelpful "unknown error" from the SMTP server. Charset is a valid IANA charset string, like "utf-8." A list of charsets are here http://www.iana.org/assignments/character-sets (note that I have no idea which of these character sets may or may not work within the context of a BizTalk orchestration/send.
If you send a plain text email by assigning the SMTP.EmailBodyText context property on a message (keep it short... the property limit is 255 chars!), you MUST set EmailBodyTextCharset on that message, or you will receive a very unhelpful "unknown error" from the SMTP server. Charset is a valid IANA charset string, like "utf-8." A list of charsets are here http://www.iana.org/assignments/character-sets (note that I have no idea which of these character sets may or may not work within the context of a BizTalk orchestration/send.
Friday, March 7, 2008
Base64Binary, XLANG, arrays, and you
The XML type xs:base64Binary maps to a byte array in the .NET framework. This is simple enough to manage in most any .NET application you can think of, but in BizTalk orchestrations, it can be a little more complicated.
The short story is this: BizTalk orchestrations are based on XLANG technology, which cannot comprehend arrays. This is why you can't create an orchestration variable of an array type. If you promote an xs:base64Binary field and try to use it anywhere in an orchestration, the framework will try to deal with it as a byte array and the orchestration will fail, even if you simply use an expression shape to hand it to a .NET method that is expecting a byte array as a parameter.
The solution is to access the field with an xpath that's wrapped in the "string()" xpath function, and manipulate it as a string variable. Maybe this means that you'll hand it to a .NET method and turn it back into a byte array. You can do whatever you want with it, except treat it as a byte array within an orchestration.
The short story is this: BizTalk orchestrations are based on XLANG technology, which cannot comprehend arrays. This is why you can't create an orchestration variable of an array type. If you promote an xs:base64Binary field and try to use it anywhere in an orchestration, the framework will try to deal with it as a byte array and the orchestration will fail, even if you simply use an expression shape to hand it to a .NET method that is expecting a byte array as a parameter.
The solution is to access the field with an xpath that's wrapped in the "string()" xpath function, and manipulate it as a string variable. Maybe this means that you'll hand it to a .NET method and turn it back into a byte array. You can do whatever you want with it, except treat it as a byte array within an orchestration.
Don't use XMLTransmit on SOAP Ports
This one's short and simple: Don't use the XMLTransmit pipeline (or any other pipeline with an XML assembler) on the send side of a SOAP port.
The SOAP adapter receives the message to send in pieces, and knows how to assemble it. The XML assembler (which really only serves a few purposes, none of which are very important, and all of which are easy to achieve via other methods or workarounds) can get confused by these pieces and will fail.
Using the XMLReceive pipeline (or any pipeline with an XML disassembler) on the receive side of a SOAP port is fine, because the adapter has already delivered the full XML message.
The SOAP adapter receives the message to send in pieces, and knows how to assemble it. The XML assembler (which really only serves a few purposes, none of which are very important, and all of which are easy to achieve via other methods or workarounds) can get confused by these pieces and will fail.
Using the XMLReceive pipeline (or any pipeline with an XML disassembler) on the receive side of a SOAP port is fine, because the adapter has already delivered the full XML message.
Subscribe to:
Posts (Atom)