Thoughts, rants and information for the like-minded.
Published on March 29, 2004 By Charles Young In Software Development
BizTalk 2004's XLANG/s language is syntactically reminiscent of C#, and it is natural to assume that it exhibits similar features. In fact, XLANG/s is very different to C#, providing specific support for process flow, whilst offering only a rudimentary set of features for expressing business logic.

It is not really necessary for developers to know the main constructs of XLANG/s because these are produced as a result of drawing orchestration diagrams in Visual Studio .NET. It is unlikely that many developers will wish to create orchestrations by writing XLANG/s directly in an .odx file, though this can be done if wished. Several of the orchestration shapes relate directly to XLANG/s constructs. For example, a ConstructMessage shape generates a 'construct' statement. Others output 'if' and 'while' statements or generate calls to pre-defined functions. From a practical viewpoint, it is, however, important to understand the syntactic differences between C# and XLANG/s when writing expressions.

At the time of writing (March 2004), there is no public documentation for XLANG/s. Hence this document comes with a health warning. It is written in good faith as a result of observation and trial & error. There is no guarantee made in regard to its accuracy or completeness. I have not documented a comparison with every feature of C#, as it is fairly obvious that many features do not apply to orchestrations. For example, you cannot declare your own classes or structs within XLANG/s, and therefore you cannot overload operators or create virtual methods. I have concentrated, instead, on those features that you might expect to be able to use within expressions and message assignment shapes.

Primitive data types

XLANG/s treats the .NET built-in types (Int32, String, etc.) as traditional primitives, and does not allow type members to be invoked. You cannot, for example, use the .NET string class to perform string manipulation. In some cases, there are alternatives. For example, the StringBuilder class has a Replace() method which you can use instead of myString.Replace(). There is no very obvious reason for this restriction, and it seems a little strange when you consider that XLANG/s does not provide aliases for its 'primitives', but uses the fully qualified .NET types names (e.g., System.Int32). The list of data types presented in the Orchestration View window when creating variables includes UI 'aliases' for all the XLANG/s 'primitives, and also includes 'datetime (System.Datetime) and 'timespan' (System.Timespan). These last two types are not 'primitives' and therefore you can invoke their methods and properties.

Arrays

XLANG/s has only the most minimal support for arrays. You can declare array variables directly within XLANG/s, although there is no support for this within the Orchestration Designer (you have to edit the .odx file directly). You cannot, however, initialise an array directly within XLANG/s. The only use for array variables I have found so far is in conjunction with 'ref' and 'out' array method parameters. In this case, after calling the method, you can then use the [ ] operator to index into the array in the normal way.

In addition, there is no support for C# 'indexers' with their array-like behaviour. When using an object that supports indexers, you must use some alternative mechanism. For example, calling SelectNodes() on an XML element returns a XmlNodeList object. You cannot index into this collection using square parenthesis. You must, instead, use the ItemOf property which is designated as the C# indexer.

'using' directive

The 'using' directive is supported within XLANG/s and can be used in much the same way as in C# (although not for aliasing). However, rather like array variable declarations, it is not supported within the Visual Studio Orchestration Designer, or within the expression editor. You can open an .odx file in a text editor and place 'using' directives within a module (directly before any services). You can then use unqualified type names to declare variables, construct new objects, etc. However, if you try to use an unqualified type names within the expression editor, you get an error message. The expression editor, in the first instance, edits a representation of your expression held in the designer XML in the .odx file, and because the editor thinks it is illegal to use unqualified type names, it places an #error directive within the actual XLANG/s code. It's a real pity the 'using' directive is not supported by the designer, as its absence makes your XLANG/s expressions more verbose and harder to read.

C#, of course, has a second use for the 'using' keyword as a statement. A 'using' block automatically disposes a designated object at the end of the block. This usage is not supported in XLANG/s

Static variables

An orchestration is itself a type. Orchestration variables are compiled to instance variables of context objects within the current orchestration. There is no support for static orchestration variables, and hence no equivalent way of sharing data programmatically across all instances of an orchestration.

Non-static fields

XLANG/s cannot access the non-static fields of objects.

Scope rules

The scope rules for variables are not entirely similar to C#. In C#, a variable in an inner scope hides a variable of the same name in an outer scope. This is not the case in XLANG/s. You cannot declare two variables of the same name in the same hierarchical scope. The reason for this is to do with the inner workings of an orchestration. Each scope is represented at runtime by a scope context object, and each variable declared at that scope level is represented by an internal field of that class. When an inner scope contains an expression that uses a variable from an outer scope, the orchestration code generator generates a field in the inner scope class of the same name and type as the corresponding field on the outer scope class. At runtime, when execution enters the inner scope, the value of the field on the outer scope object is copied to the inner scope. Then, when execution leaves the inner scope, the value is copied back. Essentially, each scope level has its own private copy of data to work on, regardless of the scope level at which variables were introduced.

All access to the internal 'variable' fields of context classes is threadsafe. The orchestration code uses read and write locks on the fields. This approach allows parallel flow paths to execute on different threads without deadlocking or contention.

Statements

The following C# statements are supported in XLANG/s:

checked
if
throw
unchecked
while

The following C# statements are not supported in XLANG/s:

break
continue
do
fixed
for
foreach
goto
lock
return
switch
try/catch/finally
unsafe
using

It appears that 'try' may be a recognised keyword in XLANG/s. It is, I suppose, either reserved for future use, or used in a context I have yet to discover. Note that 'catch' is used, without 'try' in the 'exceptions' clause of a 'body()'. 'finally' is, I think, not a recognised keyword.

Although 'lock' is not supported, it should be noted that scopes can be synchronised to ensure serialised thread access to the code contained within the scope.

Labels are not supported in XLANG/s

Operators

The following C# operators are supported:

new|<<
typeof^==
( )!!=
.~>=
+=<=
-<
*>
/&&
%||
&>>

The following C# operators are not supported:

as*=
is/=
sizeof%=
stackalloc&=
[ ]*!=
?:^=
++<<=
-->>=
+=->
-=

* There is limited support for the [ ] operator. See the section above on arrays.

Literals

The following C# literals are supported

true
false
null

Pre-Processor Directives

The following C# pre-processor directives are supported
#if
#else
#elif
#endif
#define
#undef
#warning
#error
#region
#endregion

The following C# pre-processor directive is not supported
#line

XLANG/s - specific syntax

XLANG/s provides some very specific syntax of its own that is not derived from C#. BizTalk message objects support a specialised syntax for setting 'global' context properties on messages. For example, if you are planning to send an outbound message via the SMTP adapter, you can assign 'From', 'Subject', and various other properties. To do this, you use normal parenthesis (rather than square brackets) to access these global properties. E.g.,

mySMTPMessage(SMTP.From) = "charlesyoung@joeuser.com";

Internally, this code results in a call to the SetPropertyValue method of the message object.

Miscellaneous

XLANG/s supports casting using parenthesis, but does not support the 'as' operator.

Constants and readonly variables are not supported in XLANG/s

ref and out parameters are supported. 'params' array parameters are also supported. However, the Intellisense facility in the expression editor does not recognise the use of the ref / out keywords.

The following C# access keywords are not supported

this
base

XLANG/s supports verbatim strings. It also supports escapes such as "\n" and "\r".

Evaluating expressions

XLANG/s offers a rather basic approach to expression evaluation. Specifically, it cannot assign the evaluation of one expression to another using dot syntax. For example, the following expression would be invalid in XLANG/s:

x = SomeObject.SomeMethod().SomeProperty;

Instead, you must use intermediate variables to hold sub-expression values:

interVar = SomeObject.SomeMethod();BR> x = interVar.SomeProperty;

Originally, I suspected that the XLANG/s compiler was simply taking a low-level, direct, route to turning code directly into IL with the minimum of compilation. However, now I've spent a little time looking at the IL generated over XLANG/s code, I’ve discovered that this is not the case. Indeed, as you might expect, XLANG/s is really a scripting language operating at a higher level than C#. Orchestration variables, for example, become instance fields of inner context classes embedded within the orchestration class. The following XLANG/s...

myDoc = new System.Xml.XmlDocument();

causes the following code to be emitted (shown as C# equivalent to the IL)...

// __MyOrchestration_1 is an inner context class
__MyOrchestration_1 __ctx1__ = new __MyOrchestration_1();

...

__ctx1__.__myDoc.set_UnderlyingXmlDocument(new System.Xml.XmlDocument());



In this case the __myDoc field of the __MyOrchestration_1 context class is data typed as Microsoft.XLANGs.RuntimeTypes.XmlDocumentSerializationProxy. This proxy class is very necessary, as the XmlDocument class does not support serialization (i.e., it is not annotated with the [Serializable] attribute). The proxy class acts as a wrapper for XmlDocument and provides the missing serialisation facilities. It is a pity that other DOM classes, as as XmlElement, are not wrapped in a similar way. If you want to use XLANG/s to construct an XML document programmatically using XmlElement and other DOM classes, you must place your code in a scope with an atomic transaction so that these classes will not be serialised when the orchestration is dehydrated. Alternatively, you could write your own serialisable 'proxies' in C#, or create a serialisable helper class that constructs the XML for you.

As you can see, XLANG/s is operating at a higher level than C#. I don't think there any fundamental reason, however, why the XLANG/s compiler could not handle sub-expressions using the dot operator. I guess the BizTalk team just never got around to building this level of sophistication into their XLANG/s compiler.

A simple Conclusion

Avoid using XLANG/s for 'serious' business logic. Instead, consider writing your business logic in separate C# components. These can then be invoked within an orchestration directly (give your BizTalk project a reference to your component and then instantiate objects and call methods and properties in the normal way). Alternatively, consider using the rules engine to manage business rules and invoke your home-grown business logic.



Comments
No one has commented on this article. Be the first!