Use a port name to detect the name of a BizTalk application

Just by knowing the name of a send or receive port, you can detect the name of its associated application by creating a custom pipeline component. If you don’t know the port name, have a look at one of my previous posts about how to detect port names. In case your custom pipeline is executed from an orchestration, you’ll be unable to use a port name to detect the application name. In a later post, I’ll show you how to detect the name of an activating orchestration, and use that to detect the name of the associated application.

My approach to this, was to start by creating a method that takes in two parameters.

public static string GetBTSApplicationName(string port, bool searchReceivePortsFirst)
{
}

The second parameter, searchReceivePortsFirst, is a flag that enables you to optimize the performance of the method. There are two port collections to search through, one containing receive ports, and one containing send ports. Since both receive and send pipelines can be executed on any two-way port, the parameter enables you to make a hint to the method about which collection it should search through first.

In the GetBTSApplicationName method, the first thing to do is establishing a connection with the BizTalk Management database.

using (BtsCatalogExplorer catalog = new BtsCatalogExplorer())
{
catalog.ConnectionString = GetBizTalkMgmtDbConnectionString();
}

The BtsCatalogExplorer class requires a reference to the Microsoft.BizTalk.ExplorerOM library.

Retrieving the connection string to the BizTalk Management database, requires a few steps. The following method stores the connection string the first time it’s called. Subsequent calls to the method will thus be faster.

private static string bizTalkMgmtDbConnectionString;

private static string GetBizTalkMgmtDbConnectionString()
{
if (bizTalkMgmtDbConnectionString == null)
{
string server, database;
GetBizTalkMgmtDbInfo(out server, out database);
return bizTalkMgmtDbConnectionString = "SERVER=" +
String.Concat(server,
";DATABASE=",
database,
";Integrated Security=SSPI");
}
return bizTalkMgmtDbConnectionString;
}

The GetBizTalkMgmtDbInfo method gets the name of the BizTalk Management database and the name of the database server from the Windows registry.

private static string bizTalkMgmtDbName;
private static string bizTalkMgmtDbServerName;

private static void GetBizTalkMgmtDbInfo(out string server, out string database)
{
if (bizTalkMgmtDbServerName == null || bizTalkMgmtDbName == null)
{
server = "localhost";
database = "BizTalkMgmtDb";
using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(
@"SOFTWARE\Microsoft\BizTalk Server\3.0\Administration"))
if (rk != null)
{
if (bizTalkMgmtDbServerName == null)
{
string value = rk.GetValue("MgmtDBServer") as string;
if (!String.IsNullOrWhiteSpace(value))
server = bizTalkMgmtDbServerName = value;
}
else server = bizTalkMgmtDbServerName;
if (bizTalkMgmtDbName == null)
{
string value = rk.GetValue("MgmtDBName") as string;
if (!String.IsNullOrWhiteSpace(value))
database = bizTalkMgmtDbName = value;
}
else database = bizTalkMgmtDbName;
}
}
else
{
server = bizTalkMgmtDbServerName;
database = bizTalkMgmtDbName;
}
}

Back to the GetBTSApplicationName method, we insert the following code into the using clause:

string application;
if (searchReceivePortsFirst)
{
if (!String.IsNullOrEmpty(application =
GetApplicationFromReceivePort(catalog.ReceivePorts, port)))
return application;
if (!String.IsNullOrEmpty(application =
GetApplicationFromSendPort(catalog.SendPorts, port)))
return application;
}
else if (!String.IsNullOrEmpty(application =
GetApplicationFromSendPort(catalog.SendPorts, port)))
return application;
else if (!String.IsNullOrEmpty(application =
GetApplicationFromReceivePort(catalog.ReceivePorts, port)))
return application;

This code searches the collections of receive and send ports in the order you decide.

The following are the implementations of the two methods that search the collections for the port name, and if found, return the application name:

private static string GetApplicationFromReceivePort(ReceivePortCollection collection,
string port)
{
foreach (ReceivePort receivePort in collection)
if (receivePort.Name.Equals(port, StringComparison.OrdinalIgnoreCase))
return receivePort.Application.Name;
return String.Empty;
}

private static string GetApplicationFromSendPort(SendPortCollection collection,
string port)
{
foreach (SendPort sendPort in collection)
if (sendPort.Name.Equals(port, StringComparison.OrdinalIgnoreCase))
return sendPort.Application.Name;
return String.Empty;
}

Notice that I’m using foreach to loop through the port collections. Each collection actually has an index that’s easier to use, but is much slower to execute. I don’t recommend using the index.

That’s all! Now get out there and hunt down those application names…

Advertisements
By Knut Urke

Workaround for the (N)ACK bug in BizTalk

In BizTalk Server, acknowledgments are supposed to be discarded if there are no subscribers to them. You can read more about that here.

However, in BizTalk Server 2006 R2 to 2013 (and probably newer versions, but I haven’t tested on 2013 R2 and 2016), there’s a bug causing ACKs and NACKs to be published to the MessageBox even when there are no subscribers to them.

This can be tested by creating an orchestration that sends a message to a one-way send port with delivery notifications activated. Do this in a scope that catches a DeliveryFailureException. Make sure that the delivery fails. When that happens, the message will be suspended, and the orchestration will handle the NACK message in the DeliveryFailureException catch block. Let the orchestration complete or terminate after catching the exception.

Now, if the suspended message is resumed, BizTalk will try to transfer the message, and again return an ACK or a NACK. You would then assume that the acknowledgment will not be published to the MessageBox, because the subscribing orchestration instance has been terminated and no longer subscribes to it. And you would be wrong. This time the acknowledgment is suspended due to a routing failure.

The best workaround I’ve found for this, is to create a send port using a custom null pipeline. The Execute method of the pipeline component should look like this:

public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
return null;
}

You can now subscribe to acknowledgments by adding the following filter on the send port:

  • BTS.AckType Exists

This port will now eat all acknowledgments, so you should turn off all tracking on the send port and pipeline. Additionally, you may want to set the priority on the send port to 10, which is the lowest priority setting. The value setting of this option is a bit counter-intuitive, but you can read about it here. Notice that orchestrations or other artifacts that subscribe to acknowledgments, will still receive those. And BizTalk will now behave as expected regarding acknowledgments.

As a bonus, you now have a send port that you can throw all your unwanted messages at. Enjoy!

By Knut Urke

Detect message type in a BizTalk pipeline

Sometimes it’s useful to be able to detect the message type of an XML message in a BizTalk pipeline. Here’s a stripped down code example that will do exactly that.

Not all messages are XML messages, so a little error handling or probing will be needed. You should also make sure the original stream is seekable, as the stream must be rewound.

Finally, XPathDocument expects a stream encoded in UTF-8, so you should probably do some magic to make sure your stream has the correct encoding.

public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
// Create a temporary stream, as XPathDocument will close the underlying stream.
Stream vs = null;
Stream originalStrm = pInMsg.BodyPart.GetOriginalDataStream();
try
{
originalStrm.CopyTo(vs = new VirtualStream());
vs.Position = 0;
XPathNavigator nav = new XPathDocument(vs).CreateNavigator();
string messageType = System.Convert.ToString(
nav.Evaluate("concat(namespace-uri(/*), substring('#', 1 div boolean(namespace-uri(/*))), local-name(/*))"),
CultureInfo.InvariantCulture);
}
finally
{
if (vs != null) vs.Dispose();
originalStrm.Position = 0;
}
}

This code example has been tested to work on BizTalk Server 2013 using .NET Framework 4.5 – 4.7.1. It should also work in newer versions of BizTalk Server.

(Updated on 2018.02.14 to count for message types with no namespace.)

Detect port names from BizTalk pipelines

Names of receive and send ports can be detected using a single line of code in a custom pipeline component.

In the first example, we find the name of the receive or send port that’s executing a receive pipeline. In your receive pipeline component, implement the Execute method with the following line of code.

public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
string receivePortName = pInMsg.Context.Read("ReceivePortName", "http://schemas.microsoft.com/BizTalk/2003/system-properties") as string;
}

In the second example, we find the name of the receive or send port that’s executing a send pipeline. As in the example above, we put one line of code inside the Execute method to detect the port name.

public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
string sendPortName = pInMsg.Context.Read("SPName", "http://schemas.microsoft.com/BizTalk/2003/system-properties") as string;
}

The system-level context properties used in these examples, are always populated by BizTalk or well behaving adapters, before a message is processed by a port. If the pipelines are executed from orchestrations, depending on the messages sent through the pipelines, the resulting port names will likely be null. These methods should work with BizTalk Server 2004 to 2016 and beyond.

One possible use of being able to detect port names, is logging. In a later post, I’ll show you how to use a port name to detect the name of the BizTalk Application it’s associated with.

By Knut Urke

IF statement in Message Assignment shape

Like many BizTalk developers, I learned early on that IF statements in Message Assignment shapes in orchestrations are not supported. Instead, as you may know, you must place such logical operations in a Decide shape, or use an Expression shape and carry over the result to the Message Assignment shape.

I will now challenge that fact, by introducing a way to do limited logical operations in Message Assignment shapes. And I will do this only by the power granted to me by XLANG/s. In most cases, I would not recommend this method, but it may help you lower the shape count, memory usage, size and complexity of your orchestrations.

For the sake of completeness, there are other ways to achieve logical operations in Message Assignment shapes, like calling a method, and having that return the result of a logical operation to the Message Assignment shape. I will not encourage this.

XPath 1.0

In BizTalk, including BizTalk Server 2016, there’s only support for XPath 1.0. In XPath 1.0, there’s no IF operator. Instead, you can use Jeni Tennison’s generic expression to simulate an IF ELSE test:

concat(substring($stringA, 1 div $cond), substring($stringB, 1 div not($cond)))

XPath in Message Assignment shape

XLANG/s supports the use of XPath in Message Assignment shapes. The use of XPath requires a message or a message part. If you’re not testing the content of an existing message, it may pay to use an empty message for this purpose. XPath loads the entire message in memory, which can be costly if using a large message.

Given dummyXml and dummyMsg of the System.Xml.XmlDocument type, and expr and result of type System.String, you can put the following code in a Message Assignment shape:

dummyMsg = dummyXml;
expr = "concat(substring('A', 1 div boolean('A'='A')), substring('B', 1 div not(boolean('A'='A'))))";
result = System.Convert.ToString(xpath(dummyMsg, expr));

Output: A

Example #1

The following example will show how you can exploit this method when the content of the constructed message depends on a variable.

You can imagine that the answer to a lab test comes in the form of the boolean variable approved. The message, on the other hand, shall contain the result in the form of the strings ‘Approved’ or ‘Denied’. For this example, approved is initialized with the value ‘False’.

dummyMsg = dummyXml;
expr = "concat(substring('Approved', 1 div boolean('"
+ approved.ToString()
+ "'='True')), substring('Denied', 1 div boolean('"
+ approved.ToString()
+ "'='False')))";
result = System.Convert.ToString(xpath(dummyMsg, expr));
outXml.LoadXml("<test><result>" + result + "</result></test>");
OutMsg = outXml;

The output will be:

<test>
<result>Denied</result>
</test>

Example #2

In the last example, imagine that instead of a variable, you have a message containing the test result.

<test>
<result>na</result>
</test>

If the test result is anything but ‘true’ or ‘false’, the output should be ‘Undecided’.

expr = "concat(substring('Approved', 1 div boolean(/*[local-name()='test']/*[local-name()='result']='true')), substring('Denied', 1 div boolean(/*[local-name()='test']/*[local-name()='result']='false')), substring('Undecided', 1 div not(boolean(/*[local-name()='test']/*[local-name()='result']='true' or /*[local-name()='test']/*[local-name()='result']='false'))))";

result = System.Convert.ToString(xpath(testMsg, expr));
outXml.LoadXml("<test><result>" + result + "</result></test>");
OutMsg = outXml;

The output will be:

<test>
<result>Undecided</result>
</test>

Conclusion

The possibilities for simulating IF ELSE statements using XPath are many. I’ve given you a few simple ones, representative of common scenarios.

Consider carefully the necessity of loading large messages into memory. Perhaps a better solution would be to use distinguished fields? Such a solution could look something like this:

dummyMsg = dummyXml;

expr = "substring('"
+ testMsg.result
+ "', 1 div boolean('"
+ testMsg.result
+ "'='true'))";

result = System.Convert.ToString(xpath(dummyMsg, expr));

The above code, loads an empty message into memory, and returns the test result if it’s ‘true’, or else an empty string.

Not all logical operations can be done in a Message Assignment shape using XPath. The reason behind this, is to make sure the Construct Message shape always produces the expected messages or throws an exception.

Notice as well that the code in this blog has not been optimized.

The method of simulating IF tests using XPath, can be further simplified by using XPath 2.0. Let’s hope Microsoft will support XPath 2.0 in the .NET Framework in the near future.

By Knut Urke

Manage BizTalk Artifacts from your BizTalk Application

BizTalk Server comes with a number of WMI classes for managing BizTalk artifacts. These classes can be utilized in your BizTalk application to do tasks like enabling and disabling receive locations.

In previous versions of Visual Studio, you could access these WMI classes from the Server Explorer, by going to the View menu and opening Server Explorer.

ServerExplorer

Open Server Explorer from the View menu.

From the Server Explorer, you expand Servers, find and expand your BizTalk Server, and then right click Management Classes to add the BizTalk Server WMI classes you need.

managementclasses

Expand Management Classes to find the WMI classes you’ve added.

The BizTalk Server WMI classes are found under ‘root\MicrosoftBizTalkServer’:

biztalkwmiclasses

BizTalk Server WMI classes.

Unfortunately, Microsoft decoupled the functionality of Management Classes from the IDE effective as of Visual Studio 2015. Because of this, I will show you how to generate classes using the MgmtClassGen tool. This tool can be found in the Windows SDK. My path to the tool is ‘C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\’, but yours may differ.

Open a Command Prompt with administrative privileges to the location of the MgmtClassGen tool. Then run the following command, substituting the class name and other options with what you need:

mgmtclassgen MSBTS_ReceiveLocation /N root\MicrosoftBizTalkServer /O MyNamespace /P "C:\ROOT.MicrosoftBizTalkServer.MSBTS_ReceiveLocation.cs"

It will look something like this:

mgmtclassgen

Run the mgmtclassgen tool from the command prompt with administrative privileges.

Additional options are available. Have a look by running the following command:

mgmtclassgen /?

You can read about the MgmtClassGen tool here: https://msdn.microsoft.com/en-us/library/2wkebaxa.aspx

The resulting code is not optimized, so you could benefit by polishing it, but it’ll work as is.

The concept of BizTalk applications was introduced in BizTalk Server 2006, but the BizTalk Server WMI classes have not been updated since BizTalk Server 2004. Because of this, some tasks can only be accomplished by other means, like using the Microsoft.BizTalk.ExplorerOM or Microsoft.BizTalk.Operations libraries. That, however, is not in the scope of this blog.

After generating the WMI classes, you can call methods on them. Taking the WMI class MSBTS_ReceiveLocation as an example, the following code will disable all receive locations:

foreach (ReceiveLocation rl in ReceiveLocation.GetInstances())
rl.Disable();

By Knut Urke