I've noticed that - despite a fair bit of discussion on the topic - many folks
wind up using atomic scopes simply to avoid serialization requirements for .Net
types within an orchestration. It isn't surprising this is the past of
least resistance - after all, the relevant compiler error sort of suggests this
as a fix:
error X2141: a non-serializable object type 'SomeAssembly.SomeComponent
yourComponent' can only be declared within an atomic scope or service
Recall that persistence will occur when messages are sent, when another
orchestration is started asynchronously (like via the Start Orchestration
shape), when an orchestration instance is suspended, when a host instance does
a controlled shutdown, when a "wait" state occurs (like a blocking receive or
delay shape that makes the schedule a candidate for dehydration), or when the
orchestration completes.
It will also occur at the end of a transactional scope to assist with resuming
in an unambiguous state (and executing compensation logic.)
So...although the introduction of an atomic scope prevents
serialization within the scope, it introduces serialization at the end
of the scope. This is often a persistence point (read: database hit) you
wouldn't have had to live with -- if an atomic scope was introduced purely
to get around the compiler error above.
Now...sometimes the .Net component you are using indeed requires state - and
you should go ahead and implement a serialization strategy for that case.
If the component can be stateless, then you might consider making the methods
you access "static" to avoid the compiler-enforced requirement
altogether.
There is a third case, however - where you know that serialization is not
required (given how your orchestration is structured) - but you don't own the
code for it, either. For instance, you might want to use an XmlNode to
hold the result of a SelectSingleNode call on an XmlDocument. If
your use of the XmlNode instance is confined to a single
expression shape...no state actually needs to be preserved. So, in this
case, consider wrapping the class as shown below. Here, we honor the
serialization requirement - but we don't, in fact, do any serialization
work. Now, no atomic scope is needed (avoiding the associated performance
hit). Use this technique with care - the state management facilities in
BizTalk are usually a great asset.
/// <summary>
/// It can be helpful when dealing with xpath expressions within orchestrations to
/// make use of a System.Xml.XmlNode (or derivations) without having to deal
/// with the fact that XmlNode is not serializable. If usage is confined to a single
/// expression shape, we actually won't have any serialization requirements, and so they
/// have empty implementation here.
/// </summary>
[Serializable]
public class BizTalkXmlNode : ISerializable
{
private XmlNode _xmlNode;
public BizTalkXmlNode()
{
}
public XmlNode XmlNode
{
get{return _xmlNode;}
set{_xmlNode = value;}
}
public string InnerText
{
get{return _xmlNode.InnerText;}
set{_xmlNode.InnerText = value;}
}
// This is helpful to use as a static helper method because the xpath expression in
// biztalk returns a XmlNode - so direct comparisons to string literals won't work.
// (And the expression editor won't let you get the InnerText property...)
public static string GetInnerText(XmlNode node)
{
return node.InnerText;
}
private BizTalkXmlNode(SerializationInfo info, StreamingContext context)
{
// no state intended...
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
// no state intended...
}
}