Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Wednesday, August 06, 2008

BizTalk Business Rules Engine Handy functions in .NET

Working with the BizTalk 2006 R2 BRE api, I find myself going back to this code to start of as a base. These 2 functions get ALL of the versions of the policies or vocabularies.  If you want to work with only the latest, or the published ones, change the RulesStore.Filter enum to whatever your needs.

List of Policies

   1: public void GetPoliciesList()
   2: {
   3:     Microsoft.BizTalk.RuleEngineExtensions.RuleSetDeploymentDriver breDriver =
   4:         new Microsoft.BizTalk.RuleEngineExtensions.RuleSetDeploymentDriver();
   5:     Microsoft.RuleEngine.RuleStore breStore = breDriver.GetRuleStore();
   6:  
   7:     Microsoft.RuleEngine.RuleSetInfoCollection colPolInfo = null;
   8:     colPolInfo = breStore.GetRuleSets(RuleStore.Filter.All);
   9:     foreach (RuleSetInfo pInfo in colPolInfo)
  10:     {
  11:         Trace.WriteLine(string.Format("bts- Info = [{0}].v.{1}.{2}",
  12:             pInfo.Name, pInfo.MajorRevision, pInfo.MinorRevision));
  13:  
  14:         //get the policies to extract rules
  15:         Microsoft.RuleEngine.RuleSet pol = breStore.GetRuleSet(pInfo);
  16:         Trace.WriteLine(string.Format("bts- Count = [{0}]", pol.Rules.Count));
  17:     }
  18: }

This is how to get a list of all policies published on the BRE database.  Once you get all of the RuleSets, you can loop through each of them to retrieve the actual rules behind them.  Before getting to the rules, you need to get a RuleSet out of the RuleSetInfo.


List of Vocabularies



   1: public void GetVocabulariesList()
   2: {
   3:     Microsoft.BizTalk.RuleEngineExtensions.RuleSetDeploymentDriver breDriver = 
   4:         new Microsoft.BizTalk.RuleEngineExtensions.RuleSetDeploymentDriver();
   5:     Microsoft.RuleEngine.SqlRuleStore sqlRuleStore = (SqlRuleStore)breDriver.GetRuleStore();
   6:  
   7:     Microsoft.RuleEngine.VocabularyInfoCollection colVocInfo = null;
   8:     colVocInfo = sqlRuleStore.GetVocabularies(RuleStore.Filter.All);
   9:     foreach (VocabularyInfo vInfo in colVocInfo)
  10:     {
  11:         Trace.WriteLine(string.Format("bts- vInfo = [{0}].v.{1}.{2}",
  12:             vInfo.Name, vInfo.MajorRevision, vInfo.MinorRevision));
  13:  
  14:         //get the vocabulary to extract collection of definitions
  15:         Microsoft.RuleEngine.Vocabulary voc = sqlRuleStore.GetVocabulary(vInfo);
  16:         Trace.WriteLine(string.Format("bts- Count = [{0}]", voc.Definitions.Count));
  17:     }
  18: }

This is how to get a list of all vocabularies published on the BRE database.  If you need to see how to get to all of the definitions on a particular vocabulary, see my previous post: How to access BRE Vocabularies from .NET.


Noticed that there are some subtle differences in how you retrieve each piece of information.  For Policies you get a RuleStore while to get the Vocabularies you need to get a SQLRuleStore.


As usual, feedback is always welcome if you use this code.

Tuesday, July 29, 2008

Deploying Business Rules using C# in BizTalk 2006 R2

In one of my last project, I have been working extensively with the Business Rules Engine in BizTalk. This project contains some complex Federal/State/Company/ rules. Being a Health Management Care related project I have to deal with many issues like requirements, changing policies, rules scope to a certain type, etc.

Delivering this project took some time and effort.  Not only the requirements were *agile*, but trying to keep compliance with SOX laws was a challenge.  I can summarized all of the requirements to these 3:

Requirements:

  1. Policies have to be atomic (independent from changes to other policies)
  2. Versionning of policies and being able to execute ANY version, any time.
  3. Need to know which facts were used to determine an outcome.

For the main requirement, I have created a Master Rule that determine the outcome, then I've created several *supporting* rules that will help me determine which of the rules were evaluated. The versionning requirement was already implemented by the BRE in BizTalk.

a sample of this would be something like this:

image

It quickly became very obvious that I have to deal with lots of policies and vocabularies.  If you ever tried using the Rules Deployment Wizard, you will see that it only allows you to export a SINGLE policy at a time.  Having over 500+ policies and over 30+ vocabularies  was not going to work out.

I have found the DeployRules.exe application written by Sreedhar Pelluru from Microsoft.  Here is the original article.  I took his program and modified to meet my needs.  Since I was only testing a set of policies at a time (i.e. Federal policies only), this tool provide me the ability to only load those policies that were relevant to the type I was working on. 

I know I have learned a lot about the BRE api from reading his well documented code.  With his permission, I have posted his original work and the modifications done to it back to the community at http://www.codeplex.com/DeployRules.  Yes it is still a work in progress.

Hope this help someone out there.

Wednesday, May 14, 2008

Distinguished fields of type xs:dateTime not Working on Orchestrations

Writing a spyke for a simple program, I came out with this odd behavior when I try to compile my BizTalk project.

image

I have created a simple schema, and in one of the fields I have field of type xs:dateTime. Well, when I have tried to use this field on an expression shape, I get this build compiler error:

'System.Xml.XmlDocument' does not contain a definition for 'XXXX'

where XXXX is the field name of the element that I have declared as a xs:dateTime type. I then went and set it up as a distinguished field. Here is a sample generated xml from my test schema:

  1. <ns0:Customer xmlns:ns0="http://DistingProperty.Test.Customer.v1">
    <FName>FName_0</FName>
    <DOB_datetime>1999-05-31T13:20:00.000-05:00</DOB_datetime>
    <DOB_date>1999-05-31</DOB_date>
    </ns0:Customer>

and this is the schema that I have used


image

When I am trying to use the distinguished field inside an expression shape, noticed that I get the Visual Studio IntelliSense:

image

When you try to read this value, it will always complain about the XmlDocument not being able to find the definition for the field that is defined as DateTime.

To get around this, you should use the System.Convert.ToString() instead of the .ToString() function;


1 Trace.WriteLine(" bad:[" + msgIN.DOB_datetime.ToString() + "]");

2 Trace.WriteLine("good:[" + System.Convert.ToString(msgIN.DOB_datetime) + "]");



Another lengthy way to get around this issue is to assign the distinguish field to an xmlNode and then use the xml Namespace Manager to get to the node value instead.

The code on my expression shape looks like this:


1 System.Diagnostics.Trace.WriteLine("bts- In here");

2

3 //assign values to person

4 xDoc = msgIN;

5

6 xmlnsMgr = new System.Xml.XmlNamespaceManager(xDoc.NameTable);

7 xmlnsMgr.AddNamespace("ns0", "http://DistingProperty.Test.Customer.v1");

8

9 xNode = xDoc.SelectSingleNode("/ns0:Customer/DOB_datetime", xmlnsMgr);

10 System.Diagnostics.Trace.WriteLine("bts-[" + xNode.OuterXml + "]");

11

12 xNode = xDoc.SelectSingleNode("/ns0:Customer/DOB_date", xmlnsMgr);

13 System.Diagnostics.Trace.WriteLine("bts-[" + xNode.OuterXml + "]");

14

15 //this works as expected

16 System.Diagnostics.Trace.WriteLine("bts- " + msgIN.FName);

17


Which ever way you choose, this looks like a limitation on the way the XLANG/s in the Expression shape interprets the command code.

Thursday, November 15, 2007

Enum.Parse

I had this issue, in which in one of my C# class, I had a enum type. Then I wanted to send a string to a method, which will populate this object. The problem was that the enum type was an integer, and I was passing a string down to the object.

    1 public enum ProductTypeEnum : int 
    2 {
    3     blue = 10,
    4     red = 20,
    5     green = 30
    6 } 

Here is the class that I was using to populate with values:

    1 public void GetDiscountTest_BLUE_Preferred()
    2 {
    3     OrderDetail inOrder = new OrderDetail();
    4     inOrder.ProductType = (Int32) ProductTypeEnum.blue;
    5     inOrder.Quantity = 100;

Now, I wanted to replace the code with something like this:

    1 public void GetDiscountTest_BLUE_Preferred(string sColorName)
    2 {
    3     OrderDetail inOrder = new OrderDetail();
    4     inOrder.ProductType = ConvertToEnum(sColorName);
    5     inOrder.Quantity = 100;


Here is the code to accomplish that:
    1 public void WorkingDiscount(string sColorName)
    2 {
    3     OrderDetail inOrder = new OrderDetail();
    4     inOrder.ProductType = (Int32)Enum.Parse(typeof(ProductTypeEnum), sColorName);
    5     inOrder.Quantity = 100;

now I will remember this.

Friday, September 07, 2007

How to access BRE Vocabularies from .NET

Let's assume that you need to access a Biztalk Business rules vocabulary definitions from a .NET assembly. You need to understand how the BRE API is setup to handle this.

Let's create a vocabulary with a definition constant of type string:



Now create the definition with the type of string as follows:



Exporting this vocabulary using the Business Rules Engine Deployment Wizard, the xml will look like this: [notice all 6 fields in green...]

1
<?xml version="1.0" encoding="utf-8"?>
    2 <brl xmlns="http://schemas.microsoft.com/businessruleslanguage/2002">
    3   <vocabulary id="1d3ad67e-1ed3-46ed-88a7-23199eef8fcd" name="Sample_ValidNames" uri="" description="">
    4     <version major="1" minor="0" description="" modifiedby="LocalDC\awing" date="2007-09-07T21:55:45.545-04:00" />
    5     <vocabularydefinition id="cdff8b38-e565-4942-b76f-72161449a457" name="Definition1_DefinitionName" description="Definition1_DefinitionDescription">
    6       <literaldefinition type="string">
    7         <string>Definition1_DefinitionValue</string>
    8       </literaldefinition>
    9       <formatstring language="en-US" string="Definition1_DefinitionDisplayName" />
   10     </vocabularydefinition>
   11   </vocabulary>
   12 </brl>

To access the different part of this vocabulary, I use this C# code to access all of the parts for this vocabulary definition.

Add the namespaces:

    1 using System.Diagnostics;
    2 using Microsoft.RuleEngine;

then copy the following code:

    1         public void GetAllDefinitionsFromVocabulary(string vocabularyName)
    2         {
    3             RuleStore breStore;
    4             RuleSetDeploymentDriver breDriver = new RuleSetDeploymentDriver();
    5 
    6             breStore = breDriver.GetRuleStore();
    7             Trace.WriteLine(string.Format("bts- ## Vocabulary:[{0}] ##", vocabularyName));
    8             Trace.WriteLine("bts-");
    9 
   10             VocabularyInfoCollection breVocInfoCol;
   11             breVocInfoCol = breStore.GetVocabularies(vocabularyName, RuleStore.Filter.All);
   12             foreach (VocabularyInfo oVoc in breVocInfoCol)
   13             {
   14                 Vocabulary _vocab = breStore.GetVocabulary(oVoc);                
   15 
   16                 foreach (VocabularyDefinition vocDefn in _vocab.Definitions)
   17                 {
   18                     ///string x = o["en-US"].Format;
   19                     Trace.WriteLine(string.Format("bts- Definition.Id: [{0}]", vocDefn.Id));
   20                     Trace.WriteLine(string.Format("bts- Definition.Name: [{0}]", vocDefn.Name));
   21                     Trace.WriteLine(string.Format("bts- Definition.Description: [{0}]", vocDefn.Description));
   22 
   23                     LiteralDefinition literalDefinition = vocDefn as LiteralDefinition;
   24 
   25                     //literaldefinition
   26                     if (literalDefinition != null)
   27                     {
   28                         if (literalDefinition.Value != null)
   29                         {
   30                             Trace.WriteLine(string.Format("bts- literal: [{0}]", literalDefinition.Value.ToString()));
   31                         }
   32                     }
   33 
   34                     //formatstring definition
   35                     foreach (FormatString frm in vocDefn.FormatStrings)
   36                     {
   37                         Trace.WriteLine(string.Format("bts- frm.Language: [{0}]", frm.Language));
   38                         Trace.WriteLine(string.Format("bts- frm.Format: [{0}]", frm.Format));
   39                     }
   40 
   41                     Trace.WriteLine("bts- ----------------------------------");
   42                 }
   43             }

So if I call my method with the vocabulary name = Sample_ValidNames. Then in DebugView you can see that we get all 6 fields that we set on the wizard and on the XML exported file.

    1 [5844] bts- ## Vocabulary:[Sample_ValidNames] ## 
    2 [5844] bts- 
    3 [5844] bts- Definition.Id: [cdff8b38-e565-4942-b76f-72161449a457] 
    4 [5844] bts- Definition.Name: [Definition1_DefinitionName] 
    5 [5844] bts- Definition.Description: [Definition1_DefinitionDescription] 
    6 [5844] bts- literal: [Definition1_DefinitionValue] 
    7 [5844] bts- frm.Language: [en-US] 
    8 [5844] bts- frm.Format: [Definition1_DefinitionDisplayName] 
    9 [5844] bts- ---------------------------------- 
   10 [5844] bts- Done 

There, hope this helps someone. [dont forget to post your comment if this code helps you out.. ;)