February 2015 - Posts

How to use context data with Business Rules
17 February 15 01:51 AM | wmmihaa

 

I had never come across the need to execute rules based on message metadata until a colleague of mine asked me the other day. Which is surprising as we often define routing rules based on the message context.

The basic principle is simple, – Rather than using schema based facts, we use facts based on .Net assemblies. In this case IBaseMessageContext (Microsoft.BizTalk.Message.Interop). This works well in cases where you’d like to call the rule from within a pipeline, as the context of the pipeline message (IBaseMessage) is implementing the IBaseMessageContext interface.

If, on the other hand, you want to execute the rule form an orchestration we can’t use an interface as a rule fact, as the CallRules Policy shape can’t work with interfaces. In those cases we’d need to use a custom class (the one the is used by BizTalk is internal).

Create the output fact schema

Before we create the rule you need to have your facts ready. The input fact is going to be the context object mentioned above but you need to create the output schema where you’re going to store the result if the rule evaluates to true.

Create the rule (Messaging scenario)

Open the Microsoft Business Rule Compose and create your policy and rule as you’d normally do. Then in the Fact Explorer select the .Net Classes tab, right-click the .Net Assemblies node and select Browse. The IBaseMessageContext interface is found in the Microsoft.BizTalk.Pipeline assembly. Select the assembly and expand the IBaseMessageContext in the tree view.

Drag the Read(String strName, String strNamespace) method to your rule condition:

 

 

Create the pipeline component (Messaging scenario)

If you haven’t developed a pipeline component before you can use a nuget package like Default Breeze BizTalk Pipeline Component. Simply create a class library project and install the nuget package. Follow the steps at the top of the pipeline component file before implementing the Execute method.

public IBaseMessage Execute(IPipelineContext pc, IBaseMessage inmsg)
{
    //Create a policy object based on the name of the Policy
    Policy policy = new Policy("MessageContextSamplePolicy");
            
    // Create the response document
    XmlDocument doc = new XmlDocument();
    doc.LoadXml(@"<ns0:BreTest xmlns:ns0=""http://BreWithPromotedPropertiesSample.BreTest""><Field></Field></ns0:BreTest>");
    var responseDoc = new TypedXmlDocument("BreWithPromotedPropertiesSample.BreTest", doc);
            
    //Execute the policy and pass the message context and the response docoument
    policy.Execute(new object[] { inmsg.Context, responseDoc });
            
    var responseXml = responseDoc.Document.OuterXml;
           
    // TODO
    // Take action on the response XML...

    return inmsg;
}

Create the rule (Orchestration scenario)

There are two problems to working with the context data together with orchestrations; Access the metadata and not being able to work with interfaces (CallRules Policy shape can’t work with interfaces).

Within the orchestration we don’t have the IBaseMessage from which we can easily access the context. Instead we got the XLANGMessage. The message context is not as easily accessible form the XLANGMessage as it is from the IBaseMessage. Thankfully Maxime Labelle provided insight of how this can be accomplished.

I solved these problems be creating my own class called XLANGMessageContext and from within the constructor I read through the metadata. The class also provide a Read method that will return the value for a specific property:

 

public class XLANGMessageContext
{
    Hashtable _messageContext = null;

    public XLANGMessageContext(XLANGMessage message, string name, string ns)
    {
        try
        {
            foreach (Segment segment in Service.RootService._segments)
            {
                IDictionary fields = Context.FindFields(typeof(XLANGMessage), segment.ExceptionContext);

                foreach (DictionaryEntry field in fields)
                {
                    XMessage msg = (field.Value as XMessage);
                    if (msg == null)
                        continue;

                    if (String.Compare(msg.Name, message.Name) != 0)
                        continue;

                    var key = new XmlQName(name, ns);
                    _messageContext= msg.GetContextProperties();
                       
                }
            }
        }
        catch (Exception /* e */)
        {
            // do not provoke failure
            // probably best to add some logging here
        }
    }

    public object Read(string strName, string strNamespace)
    {
        var key = new XmlQName(strName, strNamespace);
        var result = _messageContext[key];
        return result;
    }
}

Creating the rule is very similar to the messaging scenario, but instead of using the IBaseMessageContext object you’ll use the Read method from the XLANGMessageContext:

Use the XLANGMessageContext (Orchestration scenario)

In your orchestration, create  a BreHelperComponent.XLANGMessageContext valiable called xlangMessageContext, and construct it in an Expression shape:

xlangMessageContext = new BreHelperComponent.XLANGMessageContext(myMessage);

Lastly add a CallRules shape and provide the parameters:

 

You can download the sample here.

HTH

Mikael

Filed under: , ,

This Blog

News

    MVP - Microsoft Most Valuable Professional BizTalk User Group Sweden BizTalk blogdoc

    Follow me on Twitter Meet me at TechEd

    Visitors

    Locations of visitors to this page

    Disclaimer

    The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

Syndication