Check it out! New whitepaper on Transactional Adapters and Adapter Base Classes

I took a lot of your valuable reviewer feedback (thank you, see earlier post) and completed my whitepaper entitled, “Developing a Transactional BizTalk Adapter Using the Microsoft Base Adapter Classes“. You can download it at http://download.microsoft.com/download/3/7/6/376a6f6c-8c97-4ab5-9d5a-416c76793fbb/Transact_Adapter.doc.  The paper discusses at a low-level (with code snipets) transactional adapters, the Base Adapter classes and their hierarchy, and additional rarely-documented adapter design issues.  So take a look and pass it on to anyone doing adapter development.


After you read it I am very interested in your feedback – please post here if you would.   Hope this helps!


Mike

Tutorial 3 issue

BizTalk Server 2006 Tutorials > Tutorial 3: Invoice and Payment Process > Lesson 3: Connect the B2B Solution to the Supplier Web Service > 


This tutorial cannot be tested to show the use of correlations.  The SendCommomPO_Port must be configured to go to http://localhost/B2BSupplierProcessPO/Process.asmx instead of c:\tutorial\filedrop\filearchive\PO%MessageID%.xml.


 


I think that in Step 1 of Lesson 3 in the section “To configure a port that sends the PO to the supplier’s Web service” I changed the configuration, and I must set the Port Binding to Specify Now.  Additional, in the Step 6 of Lesson 3 in the numeral 22 you must remove this section:


 








SendCommonPO_Port


From the Send Ports/Send Port Groups
drop-down list, select SendCommonPO_SendPort.

This posting is provided “AS IS” with no warranties, and confers no rights.

CodeRush Love…

I’ve recently acquired a CodeRush license
for my personal use, and all I can say is … I LOVE CODERUSH!

You may be asking, “Why do you love CodeRush, Tim?” Here’s why. By using the following
sequence :

C Space P E R S O N
Click to the end of constructor block
Enter P S Space F I R S T N A M E
Click to the end of the property block
Enter P S Space L A S T N A M E
Click on class name
Ctrl-C
Click outside of the class
coll Space

Produces all of the following code:

    public class Person
    {
        
public Person(string myFileName)
        {

        }
        private string _FirstName;
        
public string FirstName
        {
            
get
            {
                
return _FirstName;
            }
            
set
            {
                _FirstName
=
value;
            }
        }
        
private string _LastName;
        
public string LastName
        {
            
get
            {
                
return _LastName;
            }
            
set
            {
                _LastName
=
value;
            }
        }
    }

    public class PersonCollection : CollectionBase
    {
        
//
public methods…
        #region Add
        
public int Add(Person person)
        {
            
return List.Add(person);
        }
        #endregion
        #region
IndexOf
        
public int IndexOf(Person person)
        {
            
for (int i
= 0; i < List.Count; i++)
                
if (this[i]
== person)    
// Found it
                    return i;
            
return -1;
        }
        #endregion
        #region
Insert
        
public void Insert(int index, Person person)
        {
            List.Insert(index,
person);
        }
        #endregion
        #region
Remove
        
public void Remove(Person person)
        {
            List.Remove(person);
        }
        #endregion
        #region
Find
        
//
TODO: If desired, change parameters to Find method to search based on a property of
Person.
        public Person Find(Person person)
        {
            
foreach (Person lPersonItem in this)
                
if (lPersonItem
== person)    
// Found it
                    return lPersonItem;
            
return null;    //
Not found
        }
        #endregion
        #region
Contains
        
//
TODO: If you changed the parameters to Find (above), change them here as well.
        public bool Contains(Person person)
        {
            
return (Find(person)
!=
null);
        }
        #endregion

        //
public properties…
        #region this[int
aIndex]
        
public Person this[int index]
        {
            
get
            {
                
return (Person)List[index];
            }
            
set
            {
                List[index]
=
value;
            }
        }
        #endregion
    }

Programmatically Create Windows Workflow Rules

I am currently working with a client that is evaluating the Windows Workflow Rules Engine.  One of their requirements is that they need to be able to create the rules programmatically through their own application.  First we looked at the ability to host the Windows Workflow rules dialog boxes in our application.  We can accomplish this through the RuleSetDialog object and the RuleConditionDialog object.  Second we looked at how we could create our own UI and programmatically create the rules through an API.  Based on the requirement of the application they have decided to use their own UI.  So, the search began on how we were going to create these rules.


 


The Workflow Rules Engine API works through the CodeDom system.  This makes a lot of sense since we are using code to create code – which is exactly what CodeDom was designed for.


 


I have used a sample fictitious rule that represents the type of rule that will be created through the UI.  The rule we need to create (and the one used in the example below) is a rule that checks the model and year of a car along with the gas mileage and assigns a surcharge, if necessary. 


 


In looking at the code below I have set up private variables to hold our data.  This could have easily been a separate object (and will be in real life).  Once I have my variables I need to setup the CodeDom objects which will reference the variables that I will be using in my rules. 


 


The CodeDom objects that are supported in the rules object model are:




  • CodeThisReferenceExpression



  • CodeArrayIndexerExpression



  • CodeAssignStatement



  • CodeBinaryOperatorExpression



  • CodeCastExpression



  • CodeDirectionExpression



  • CodeExpressionStatement



  • CodeFieldReferenceExpression



  • CodeIndexerExpression



  • CodeMethodInvokeExpression



  • CodeMethodReferenceExpression



  • CodePrimitiveExpression



  • CodePropertyReferenceExpression



  • CodeTypeReference



  • CodeTypeReferenceExpression


 


Next, I start the rule creation code.  I create a RuleSet object which I will add Rule objects to.  After I create the Rule object then I create the conditions which will be added to the rule using the Rule.Condition property(handing it a RuleExpressCondition object).  After that I will add the action using the Rule.ThenAction.Add Method (handing it a RuleStatementAction object).  I can also add the Else action using the Rule.ElseActions.Add method.  Before I can add the condition I need to create the code to represent the predicate parts that will be added to the condition.  In the code this can be seen starting with the code to create the predicate for GasMileage.  I create a CodeBinaryOperatorExpression object and then using the Left, Operator and Right properties to assembly the predicate.  I continue this for each of the predicates I will need for this rule (in this case there are 3).  I have shown in the code how to compare against an integer (with the GasMileage predicate), against a string (with the ModelYear predicate) as well as against an Enum type (with the CarCategory predicate).  These predicates also show how to use the CodeBinaryOperatorType object with both the .ValueEquality and .LessThan operators.  An interesting find is that there is no inequality operator.  To find inequality you need to create the syntax as (CarCategory == “Sports”) == false.


 


Once I have created the predicates that represent each of the parts of my rule I then need to join the predicates and create the condition which will be passed to a RuleExpressionCondition object.  As seen in the code below I take the first two predicates; the ruleGasMileageTest object and using a BooleanAnd operator join to the ruleCarCategoryTest object.  If I only had two predicates I would be done and could add the condition object to the rule (carChargeRule).  Since I had three predicates I need to take the condition object created from the joining of the first two predicates and join them to the third predicate (ruleModelYearTest) to create the final condition (ruleCondition2) which will be added to the rule.


 


At this point we have the ‘if’ portion of the rule complete.  We now need to create the ‘then’ portion.  This is accomplished using the Rule.ThenAction.Add method as seen at the bottom of the code.


 


After you have looked at the code make sure to look at the image of the locals window which follows the code.  This screen shot shows the rules code that all of this CodeDom code created.


 


 


 


public class ……


{


//Object Items


private string CarName = “Jaguar”;


private string CarModel = “XK SLE”;


private string ModelYear = “2009”;


private CarCategory carCategory = CarCategory.SportsCar;


private int GasMileage = 12;


private double SurchargeRate = 0;


 


 


private void BuildRuleSet()


{



RuleSet surchargeRuleSet = new RuleSet(“SurchargeRuleSet”);


 


// Define property and activity reference expressions through CodeDom functionality


CodeThisReferenceExpression thisRef = new CodeThisReferenceExpression();


CodeFieldReferenceExpression CarNameRef = new CodeFieldReferenceExpression(thisRef, “CarName”);


CodeFieldReferenceExpression CarCategoryRef = new CodeFieldReferenceExpression(thisRef, “carCategory”);


CodeTypeReferenceExpression CategoryEnumRef = new CodeTypeReferenceExpression(typeof(CarCategory));


CodeFieldReferenceExpression ModelYearRef = new CodeFieldReferenceExpression(thisRef, “ModelYear”);


CodeFieldReferenceExpression SurchargeRef = new CodeFieldReferenceExpression(thisRef, “SurchargeRate”);


CodeFieldReferenceExpression GasMileageRef = new CodeFieldReferenceExpression(thisRef, “GasMileage”);


 


// IF GasMileage < 15 AND CarCategory = Sports AND ModelYear = 2009


// THEN Surcharge = $500


Rule carChargeRule = new Rule(“CarChargeRule”);


surchargeRuleSet.Rules.Add(carChargeRule);


 


// define first predicate: GasMileage < 15


CodeBinaryOperatorExpression ruleGasMileageTest = new CodeBinaryOperatorExpression();


ruleGasMileageTest.Left = GasMileageRef;


ruleGasMileageTest.Operator = CodeBinaryOperatorType.LessThan;


ruleGasMileageTest.Right = new CodePrimitiveExpression(15);


 


// define second predicate: CarCategory = Sports


CodeBinaryOperatorExpression ruleCarCategoryTest = new CodeBinaryOperatorExpression();


ruleCarCategoryTest.Left = CarCategoryRef;


ruleCarCategoryTest.Operator = CodeBinaryOperatorType.ValueEquality;


ruleCarCategoryTest.Right = new CodeFieldReferenceExpression(CategoryEnumRef, “SportsCar”);


 


// define third predicate: ModelYear = 2009


CodeBinaryOperatorExpression ruleModelYearTest = new CodeBinaryOperatorExpression();


ruleModelYearTest.Left = ModelYearRef;


ruleModelYearTest.Operator = CodeBinaryOperatorType.ValueEquality;


ruleModelYearTest.Right = new CodePrimitiveExpression(“2009”);


 


// join the first two predicates into a single condition


CodeBinaryOperatorExpression ruleCondition = new CodeBinaryOperatorExpression();


ruleCondition.Left = ruleGasMileageTest;


ruleCondition.Operator = CodeBinaryOperatorType.BooleanAnd;


ruleCondition.Right = ruleCarCategoryTest;


 


// join the third predicate into the condition


CodeBinaryOperatorExpression ruleCondition2 = new CodeBinaryOperatorExpression();


ruleCondition2.Left = ruleCondition;


ruleCondition2.Operator = CodeBinaryOperatorType.BooleanAnd;


ruleCondition2.Right = ruleModelYearTest;


 


carChargeRule.Condition = new RuleExpressionCondition(ruleCondition2);


 


// add the action: Surcharge = 500


CodeAssignStatement ruleSurchargeAction = new CodeAssignStatement(SurchargeRef, new CodePrimitiveExpression(500));


carChargeRule.ThenActions.Add(new RuleStatementAction(ruleSurchargeAction));


 


// Add the ruleset


RuleDefinitions ruleDef = new RuleDefinitions();


ruleDef.RuleSets.Add(surchargeRuleSet);


 


// Set the RuleDefinitions on the workflow


this.SetValue(RuleDefinitions.RuleDefinitionsProperty, ruleDef);


}


 


public enum CarCategory


{



SportsCar = 0,


Sedan = 1,


SUV = 2,


…….


}


 


 



 

BTSDEPLOY on a remote BiztalkMgmtDb database

Do you think you can use BTSDeploy on a remote BiztalkMgmtDb database server?

I thought it will after looking at its parameter list, it takes a database server name and managment database name and an XML port configuration file as arguments. So, it sounded very normal for me to take it for granted we can IMPORT/EXPORT stuff into remote Biztalk server (or groups) using the BTSDEPLOY command.

Example:
BTSDEPLOY EXPORT SERVER=ACCD12WS229 DATABASE=BiztalkMgmtDb BINDING=c:\ports.xml UNBOUNDPORTS

We architected everything based on this assumption.The idea of our project is to control multiple biztalk environments (groups) from a single web application. The project I’m working on is relatively of big scale where we have around 10 different Biztalk server groups each with 4 Biztalk servers + all those additional SQL servers, Clusters blau, blau.

As a developer I was working on my workstation specifying my local workstation name as server and BiztalkMgmtDb as database parameter. For quite long time we didn’t hit any problems. Once in a while we connected to some other developer boxes remotely to do some testing and everything worked seamlessly. (Yes! it worked seamlessly connecting to a remote biztalk server).

Hang on before you start cursing, why is this post then?!! All our problems started when we deployed our solution into the module testing environment. Suddenly we saw this error comming up

Access Denied. Microsoft.Adapter..WSE.dll

There are so many attributes which can go wrong and produce this error, some of them we considered.
1. It’s a ASP.NET web applicaiton, so some of the credentials may not be correct. (We used application pool running under specified Biztalk account).
2. You are accessing a remote Biztalk Database server, where some SSO bits involved. So, again some credentials may not be correct.
3. There could be some problem in MSDTC between the ASP.NET server and reomote DB server

etc,etc…we can think of so many attributes because of the distributed nature of Biztalk itself and thing what we were trying to achive.

Luckily, this error popped up on one of the developer box as well, where he was using one of our inbuild custom adapters. Then we came to a conclusion, this problem occurs only for biztalk servers(groups) where some custom adapters are deployed (BTW, WSE adapter is also an addon adapter on top of Biztalk base build).

If you deploy the same adapter in the main server** and run the command against a remote server, it IMPORTED/EXPORTED successfully. Wow! so what’s going on underneath the scene.

Here you go. Thanks to Microsoft folks Angus foreman and his upline MS staff at Redmond for making us understand what going on beneath the scene.

When you EXPORT configuration from BiztalkMgmtDb, the result set comes as a big blob data to the main Biztalk Server** in an encrypted form. Then the main server** parse the result with the help of adapters deployed in the main server**, from where it gets all the design time parameters of the adapters from the registry and generates the output port configuration xml file. If the adapters are not present in the main server** but the data is present in the blob then there is a mismatch and BTSDEPLOY throws Access denied error. We wouldn’t have found out this error or the functionality behind if there were no custom adapters.

Then, we did some work around to fix the problem, using web services and stuff. Huh! this post is already to long.

**(from where the BTSDEPLOY command is executed)