Okay,
So You want to know if you started this particular instance of an orch before or not; here’s the situation: you have a business process, upon reaching a certain condition you spawn off an orch, simple eh? Remind me of the old threading days however here is the challenge your business process is layed over in away that this condition might repeat itself, raises a good question.. have we started this orch before or not. In a server programming model oyou would have kept a handle for the thread or (app domain or process) somewhere in a static variable and always check it before starting it.
However in BTS it is completely stateless, I hear the voice in my head (as in yours) saying dude save the state in the database some where, well looks like the perfect solution (the cost of one straight forward DB call isn’t really much after all). Come to think of it we do have a state DB by default (yes I mean this MsgBox DB), can we just use it and check against it. The answer is yes using
- Direct DB access (DON’T DO THAT AT HOME style, again).
- Using subscription engine! And that is where I am going to leverage on
Anyone familiar with BTS subscription viewer, that small utility that we use to debug our business processes shows us what orchs ports is waiting for what(and their correlations as well). Reflecting that tool showed that it is using Biztalk.RulesEngine assembly.. NIIICE!. We just need to get the same data dump filter it and check against it.
Let us walkthrough the subscription viewer: it is an EXE application.
Single window has a
- Data-grid (displays the subscription record)
- A tree view that shows the details of the subscription (which port, which web method name which correlation and it is value etc…).
Filtering the code showed a single method with the name of LoadSubscriptions that loads all the subscription the code is like:
public void LoadSubscriptions()
{
this.m_Subscriptions.Clear(); // Clears the datagrd
this.PredicateLists.Nodes.Clear(); // clears the tree view
SubscriptionRuleStore store1 = new SubscriptionRuleStore("Subscription Store"); // connect to the store
this.m_rs = store1.GetRuleSet(new RuleSetInfo(this.m_GroupName, 0, 0)); // GroupName is always -> = "BizTalk Group"; (in the CTOR of the form)
foreach (Rule rule1 in this.m_rs.Rules.Values) // enumerate the list – previous call is too slow!
{
// Basiclly what is happening here is creating a data table contains all the subscription and binding the grid to it.
string text1;
DataRow row1 = this.m_Subscriptions.Tables["Subscriptions"].NewRow();
row1["SubscriptionID"] = rule1.Name;
row1["Priority"] = rule1.Priority;
UserFunction function1 = (UserFunction) rule1.Actions[0];
ClassMemberBinding binding1 = (ClassMemberBinding) function1.Binding;
Constant constant1 = (Constant) binding1.Arguments[0];
row1["Name"] = constant1.Value.ToString();
constant1 = (Constant) binding1.Arguments[1];
if ((text1 = constant1.Value.ToString()) == null)
{
goto Label_0186;
}
text1 = string.IsInterned(text1);
if (text1 != "3d7a3f58-4bfb-4593-b99e-c2a5dc35a3b2") // Some stuff are canceled from the view, my guess internal BTS pluming stuff
{
if (text1 == "59f295b0-3123-416e-966b-a2c6d65ff8e6")
{
goto Label_0162;
}
if (text1 == "226fc6b9-0416-47a4-a8e8-4721f1db1a1b")
{
goto Label_0174;
}
goto Label_0186;
}
row1["ServiceType"] = "MSMQT";
goto Label_019D;
Label_0162:
row1["ServiceType"] = "EPM";
goto Label_019D;
Label_0174:
row1["ServiceType"] = "XLANG";
goto Label_019D;
Label_0186:
row1["ServiceType"] = constant1.Value.ToString();
Label_019D:
constant1 = (Constant) binding1.Arguments[2];
row1["ServiceID"] = constant1.Value.ToString();
constant1 = (Constant) binding1.Arguments[3];
row1["InstanceID"] = constant1.Value.ToString();
constant1 = (Constant) binding1.Arguments[4];
row1["PortID"] = constant1.Value.ToString();
constant1 = (Constant) binding1.Arguments[5];
if ((text1 = constant1.Value.ToString()) != null)
{
text1 = string.IsInterned(text1);
if (text1 != "0")
{
if (text1 == "1")
{
goto Label_029A;
}
if (text1 == "2")
{
goto Label_02AC;
}
if (text1 == "3")
{
goto Label_02BE;
}
}
else
{
row1["Enabled"] = "Disabled";
}
}
goto Label_02CE;
Label_029A:
row1["Enabled"] = "Enabled";
goto Label_02CE;
Label_02AC:
row1["Enabled"] = "Deactivated";
goto Label_02CE;
Label_02BE:
row1["Enabled"] = "AdminDeactivated";
Label_02CE:
constant1 = (Constant) binding1.Arguments[6];
row1["ApplicationName"] = constant1.Value.ToString();
this.m_Subscriptions.Tables["Subscriptions"].Rows.Add(row1);
}
if (this.m_rs.Rules.Count > 0)
{
this.SubDataGrid.Select(0);
this.SubDataGrid.CurrentRowIndex = 0;
this.SubDataGrid_CurrentCellChanged(null, null);
}
}
Okay the previous code load s the subscriptions but does nothing else, upon cell change on the grid it displays the detils of the subscription as
this.PredicateLists.Nodes.Clear();
int num1 = 0;
int num2 = 0;
Rule rule1 = (Rule) this.m_rs.Rules[this.SubDataGrid[this.SubDataGrid.CurrentRowIndex, 1]];
if (rule1.Conditions is LogicalAnd)
{
this.PredicateLists.Nodes.Add(new TreeNode("AndGroup0"));
LogicalAnd and1 = (LogicalAnd) rule1.Conditions;
for (num1 = 0; num1 < and1.Arguments.Count; num1++)
{
this.PredicateLists.Nodes[0].Nodes.Add(this.GetPredicateDisplayString(and1.Arguments[num1]));
}
}
else if (rule1.Conditions is LogicalOr)
{
LogicalOr or1 = (LogicalOr) rule1.Conditions;
for (num1 = 0; num1 < or1.Arguments.Count; num1++)
{
this.PredicateLists.Nodes.Add(new TreeNode("AndGroup" + num1));
if (or1.Arguments[num1] is LogicalAnd)
{
LogicalAnd and2 = (LogicalAnd) or1.Arguments[num1];
for (num2 = 0; num2 < and2.Arguments.Count; num2++)
{
this.PredicateLists.Nodes[num1].Nodes.Add(this.GetPredicateDisplayString(and2.Arguments[num2]));
}
}
else
{
this.PredicateLists.Nodes[num1].Nodes.Add(this.GetPredicateDisplayString(or1.Arguments[num1]));
}
}
}
else
{
this.PredicateLists.Nodes.Add(new TreeNode("AndGroup0"));
this.PredicateLists.Nodes[0].Nodes.Add(this.GetPredicateDisplayString(rule1.Conditions));
}
this.PredicateLists.ExpandAll();
}
Cool eh? Well what you need to know is your subscription will always contain data, additions to what you have in mind, stuff that has to do with Port IDs. However if you know the correlation attribute name, value, orchestration name and assembly name it is enough information that you can use to answer the question raised (did I start this one before or not). Be aware you may have multiple subscription for the same correlation if it is being used so many times in your process on different ports so make sure that you filter using the correct information.
Final words:
- The subscription viewer in itself is a slow tool because it dumps all the subscription. I spent sometime trying to find any way to filter the data rather than killing the database but couldn’t find any so far. I am sure there is since BTS is very fast evaluating messages against subscriptions.
- SubscriptionRuleStore is not a supported by BizTalk so whatever you write here is write it at your own risk.