Showing posts with label Business Rules Framework. Show all posts
Showing posts with label Business Rules Framework. Show all posts

Friday, November 6, 2009

Dynamic Updates in Windows Workflow Foundation (WF)

As we know that workflows created using Windows WF are especially useful for long running processes. If during the execution of these long running processes, any update is required in workflow then we seem to have no option other than aborting the workflows and running them again after making the necessary updates. But you would be glad to know that WF provides us the facility to update a running instance of workflow without aborting it. This called Dynamic Updates in WF.


It must be remembered that the target audience of Windows WF is developers. It has never been intended for end-users. So by dynamic updates, we mean a provision in the framework to provide developer the flexibility of updating any instance of a workflow.


Though it is still a primitive update API but still we can use it for many of our requirements.


What can be updated?

In this discussion, we would be discussing following updates to the workflow instance:

  1. Addition / removal of activities in the workflow
  2. Updating declarative rules


Sources of updates:

There might be following sources from where we can update the workflow:

  1. Hosting application
  2. Within the workflow


WorkflowChanges object

To update the workflow, a WorkflowChanges object is created. This provides a working copy of workflow instance. This can be instantiated by passing in the IRootActivity of your already running workflow in the constructor.


All the requested changes made to the transient workflow, available through WorkflowChanges object, would still be part of this until you apply the changes. In that case the running workflow instance would be updated with all the changes.


WorkflowChanges object allows you to make offline changes to the skeleton copy of the workflow instance. We can make all the changes in this copy and can apply all the changes to the workflow instance at once. This is like a batch update. This would make sure that workflow instance is not busy updating itself for all the transient changes which we might want to roll over.


Restricting Dynamic Updates:

Dynamic updates are good but we don’t want our workflows to be modified very frequently by any users. We want to verify if those changes are legitimate before applying them.


In Windows WF, we can check for special conditions before allowing any changes to our workflow instance. If the changes are not able to fulfill some condition then it could never be applied to our workflow.


For this purpose, there is a property in workflow. This is DynamicUpdateCondition property. We can specify either code or declarative condition for this. Before making update, the workflow runtime would check this condition. If successful then it would allow modification of the workflow instance.


There might be specific conditions you want to check before allowing any application to be modifying your workflow. As in all the conditions, there are two options possible:

  1. Code Condition
  2. Declarative rule condition


Since we know that by dynamic updates, we can modify rule definitions. It would be interesting to check if we can modify DynamicUpdateCondition using dynamic updates. If that is the case then we can turn off this condition whenever we desire.


Validating Structural Changes:

All these conditions are checked when ApplyWorkflowChanges method is called from WorkflowChanges object. In case of failure to verify the conditions, the updates are not applied and an exception of type InvalidOperationException is thrown.


It must be remembered that before applying the changes, they can be validated using validate() method available in WorkflowChanges object. This validates all the structural changes made to the workflow. In case of failed validations, this would return a collection of ValidationError objects. Remember that it would not be verifying the updates as defined in DynamicUpdateCondition within the workflow. So there might be cases when you could validate the changes but would not be able to apply them because of failing the required conditions for such updates.


Namespaces used:

Mainly the following classes in the following namespaces are used for making dynamic updates to the rules:

  1. System.Workflow.Activities.Rules
  2. System.CodeDom

Limitations:

As discussed in the beginning, there are limitations to the updates allowed using Dynamic updates option of Windows WF. I am discussing two which I don’t like.


Adding event handlers:

It is not possible to add code to the event handlers of activities at runtime. E.g. we can not add CodeActivity and assign its Execute event handler to some delegate or lambda expression.


CodeActivity ca = new CodeActivity("MyCodeActivity");

ca.Description = "My Code activity";

ca.ExecuteCode += (sender2, e2) => Console.WriteLine(ca.Description);


The above code would result in an exception when this activity is added to the transient workflow and changes are applied to the workflow instance. This results in serialization issue.


Updating Condition Property of Activity at Runtime

It is not possible to update the condition property of activity at runtime. It releases in an InvalidOperationException exception. This is generally not a big issue because we can change the definition of condition in rules file. So generally it is not needed to reference it to a new definition.


Listing 1: (Creating a new activity in a running workflow instance)

Let us create a sample workflow console application (C#) and name it ConsoleWFAcitiviesDynamicUpdate. The template project would provide a code workflow class and Program.cs, which would instantiate the workflow.

In main method in Program.cs, insert the following code before starting the workflow instance:


System.Workflow.ComponentModel.WorkflowChanges wc = new System.Workflow.ComponentModel.WorkflowChanges(instance.GetWorkflowDefinition());

DelayActivity da = new DelayActivity("myDelayActivity");

da.Description = "My description";

da.TimeoutDuration = new TimeSpan(300);

wc.TransientWorkflow.Activities.Insert(0, da);

try

{

wc.Validate();

instance.ApplyWorkflowChanges(wc);

}

catch (Exception ex)

{

Console.WriteLine(ex.Message);

}


This would add a delay activity in the workflow instance and set the duration of delay as 300. Don’t forget to import Activities namespace in Program.cs using the following statement:


using System.Workflow.Activities;


To verify that it has created the delay activity in the workflow, we go to the workflow, workflow1.cs and create initialize even handler and insert the following code in its body:


Array.ForEach<Activity>(this.Activities.ToArray(), (ac) =>

{

Console.WriteLine(ac.Name);

}

);


Now run the project. You should be able to see “myDelayActivity” in the console window. This code is displaying the name of each activity in the workflow. This is using lambda expression syntax in C#.


You can download this project here: ConsoleWFAcitiviesDynamicUpdate1.zip




Listing 2: (Removing an existing activity)

We create a Console WF application named ConsoleWFModifyActivity and insert a delay activity in the workflow named “mydelayActivity”.

Now insert the following code in main method in Program.cs after creation of workflow instance but before starting it.

System.Workflow.ComponentModel.WorkflowChanges wc = new System.Workflow.ComponentModel.WorkflowChanges(instance.GetWorkflowDefinition());

DelayActivity da =

(DelayActivity)wc.TransientWorkflow.GetActivityByName("mydelayActivity");

da.TimeoutDuration = new TimeSpan(300);

try

{

wc.TransientWorkflow.Activities.Remove(da);

instance.ApplyWorkflowChanges(wc);

}

catch (Exception ex)

{

Console.WriteLine(ex.Message);

}


The above code is getting the delay activity from the WorkflowChanges object and removing it.


To verify if the code works, generate handlers in workflow workflow1.cs. Insert the following code in the intitialize handler:


Array.ForEach<Activity>(this.Activities.ToArray(), (ac) =>

{

Console.WriteLine(ac.Name);

}

);


This code prints the name of all activities in the console window. Since before starting the workflow instance, we have already removed the delay activity, the console window should be blank after the execution completes.


You can get this project from here: ConsoleWFModifyActivity.zip



Listing 3: (Replacing an existing activity)

Windows WF does not directly make this option available using DynamicUpdate but since we have seen in Listing 1 and 2 that we can add and remove any activity at any location. We can combine both to replace an activity. We just have to follow the following steps:


  1. Create a new activity

System.Workflow.ComponentModel.WorkflowChanges wc = new System.Workflow.ComponentModel.WorkflowChanges(instance.GetWorkflowDefinition());

CompositeActivity parentActivity = wc.TransientWorkflow;

DelayActivity newDelayActivity = new DelayActivity("myNewActivity");

newDelayActivity.Description = "My New description";

newDelayActivity.TimeoutDuration = new TimeSpan(300);


  1. Find the required activity to be replaced with the help of GetActivityByName method in TransientWorkflow.

DelayActivity exsitingDelayActivity =

(DelayActivity)parentActivity.GetActivityByName("existingDelayActivity");


  1. Find the index of the activity using different Find… methods available in Activities collection in TransientWorkflow.


int index = parentActivity.Activities.FindIndex((ac) => (ac == exsitingDelayActivity));


  1. Remove the existing activity


parentActivity.Activities.Remove(exsitingDelayActivity);

  1. Insert the new activity at the same index.

parentActivity.Activities.Insert(index, newDelayActivity);

instance.ApplyWorkflowChanges(wc);


To verify, you can insert the following code in initialized event handler of your workflow.


Array.ForEach<Activity>(this.Activities.ToArray(), (ac) =>

{

Console.WriteLine(ac.Name);

});


You can download the example project here: ConsoleWFReplaceActivity



Listing 4: (Updating a declarative Business rule)


We create a console workflow project and name it as WFDynamicRuleUpdate.


Now we create an integer MyNumber. This is defined to be used in the dynamic rule for the IfElse activity that we would create later.


public Int32 MyNumber { get; set; }


We drag and drop an IfElse activity from the toolbox to the designer window and name it as ifElseActivity1. We update the name of “if” branch activity to IfBranchActivity and “else” branch activity to “ElseBranchActivity”.


Now we drag and drop code activities in both of them.


We select the Condition property of “IfBranchActivity” and select “Declarative Rule Condition”. After updating its name as “Condition1”, we define the following rule:


this.MyNumber > 100


We create the following event handler for Code activity nested in the “IfBranchActivity”:


private void codeActivity1_ExecuteCode_1(object sender, EventArgs e)

{

Console.WriteLine("If code executed");

}


We create the following event handler for Code activity nested in the “ElseBranchActivity”:


private void codeActivity2_ExecuteCode(object sender, EventArgs e)

{

Console.WriteLine("Else code executed");

}


Now run the workflow. It would “Else code executed” print on the console.


We generate event handlers for the workflow and insert the following code in the initialize event handler:


WorkflowChanges wfChanges = new WorkflowChanges(this);

RuleDefinitions definitions = (RuleDefinitions)wfChanges.TransientWorkflow.GetValue(RuleDefinitions.RuleDefinitionsProperty);

RuleExpressionCondition cond = definitions.Conditions["Condition1"] as RuleExpressionCondition;

CodeBinaryOperatorExpression ex = cond.Expression as CodeBinaryOperatorExpression;

ex.Right = new CodePrimitiveExpression(2);

try

{

ValidationErrorCollection vr = wfChanges.Validate();

Array.ForEach(vr.ToArray(), (x) => Console.WriteLine(x));

if (vr.Count == 0)

{

ApplyWorkflowChanges(wfChanges);

}

}

catch (Exception exc)

{

Console.WriteLine(exc.Message);

}


The above code get the condition object as defined in the workflow rules. It updates the rule so that instead of checking it for 100, now it is checking it for 2.


Running the workflow executes prints “If code executed” on the console.


You can download the project from: WFDynamicRuleUpdate

Sunday, October 25, 2009

Basic features of a good Business Rule Framework

While working on business logic side of our application, it is often advised to have a centralized mechanism for management and execution of business logic. I have thought about some of the features that this Business Rule Framework should have. Having the features before hand would provide ease in evaluating various Business Rule frameworks available in the market.

While reviewing the text, I could see some of the overlaps between various features. But, I think, it is good to keep them separate.

1. Nesting and Sequencing of rules: The rule framework must provide us the ability to nest rules. This would allow us to decide the nested rules to be selected based on certain other rules.

2. Logical relationship between rules: For a condition, all rules should be satisfied or any rule should satisfy. In technical terms we have certain logical relationships like AND, OR etc. We want our rule engine to be providing such conditions.

3. Enabling / Disabling the rules: We should be able to enable / disable a rule under any circumstance. It means that we don’t have to delete the definition of rule but just disable it somehow so that it is not applied.

4. Rule Profiles: I don’t know this should be the name of title that I want to discuss. Let me explain what I have in mind, the rule engine must be able to decide application of rules based on the user / role / group. Its designer and engine should be able to access directory service. This would not only make the life of architect easier coz you wouldn’t have to decide in the code to select rule, this service would automatically be provided by the rule framework.

Doing so, it should be able to access the directory services so that It could select groups / users and design / apply rules based on their roles.

This profile can also be selected based on the source type. This source might include regions, devices. This is because certain regions and countries might have different rules e.g. financial and our rule framework must be able to see the source and decide which rules should be applied accordingly.

5. Business User interface: The rule framework should provide ability to provide some interface to business user so that not only they could understand the rules easily (language should be easy) but they might also be able to update those rules.

The system should also give the business users ability to create new rules and inject them at the required step.

6. Dynamic Updates: No compilation and build should be required to update business rules. It should be dynamic. As soon as rules are updated, the effect should be visible in the application.

7. Communication abilities: The business rule framework must provide abilities so that it could communicate with the hosting environment both ways. This feature should be available as desired in addition to the instant when they are called.

It should also provide ability so that it can communicate outside the hosting environment i.e. it could access any service across the available networks supporting the service oriented architecture.

8. Documentation Support: Business rules System should provide documentation feature. This can be stored as metadata of the business rules. It should allow different views of this documentation for business and technical users.

Users should be allowed to extract information from the documents by exporting them to formats generally used by users like word, CHM and Visio diagrams etc.

9. Instrumentation: This should support instrumentation so that logs can be created. It should support major instrumentation frameworks like WMI. It should allow different logging mechanisms like email, Windows Event log, log files etc.

Users should be allowed to define the formats of these logs. They should be allowed to define what information should be logged. The framework should provide certain variables like source information (user and machine), time of day etc.

10. Events for Rule Execution: There should be events for rule execution. The events might be fired on the basis of pre or post execution of rules. We might create other events which we can think of. In those events, we might be able to cancel the rules if certain condition is not satisfied.

11. Reliability and Availability: The rule engine should be reliable. It should allow consistency for users. It might also support redundancy as part of its design in case off any failure. Keeping single Rule Engine Server would introduce the Single Point of Failure in the design.

12. Storage Options: These rules might be stored in any or the combination of database, XML, compiled code options. Updating the storage options should not be visible to the client applications.

13. Seamless Just in Time Deployment: Clients must not be bothered when these rules are updated and deployed. This operation should be seamless.

This is really tricky because there would be some users which would already be executing rules at any instance.

14. Versioning of rules: System should support to maintain versions of these rules. Administrators should be allowed to maintain these versions. Client applications should be given flexibility to specify any particular version or latest version (as desired) for these rules.

15. Security: The rules must be secure. There should be some authentication mechanism. This would secure the rules from being updated by unwanted users. There should be category of rules which should be allowed to be updated by certain roles. These groups and users might be the same users as in the directory services.

16. Available for different Programming platforms: Organizations evolve. Some of the applications lose their values with time. So they are replaced with new systems. Doing that, these organizations have many applications running at the same time. They are developed in different programming platforms. (.net, java etc). The rule engine must be open to accept and services the requests from any platform. Being service oriented is the key for this.

17. Scalability and Performance: Response time is the key in in current businesses because we don't want our users to wait for the response drumming the keyboard. The system should be scalable to the number of users. This might provide provide documented hardware specifications based on the number of users. For good performance, the engine must be as light weight as possible for faster execution of rules.

18. Adaptable workflow for rules management (Approval mechanism) and Audits: We can not trust an individual for managing all the rules in every application in the entire organization. There should be some kind of approval mechanism so that updated rules are monitored and controlled. There should be detailed history of these approvals.

19. Pre-production release of rules: Organizations generally have three different environments. First is development in which the applications are developed. After development is complete, they are released for User Acceptance Testing (UAT). Once they pass UAT, they are released to Production environment. Our rule system must allow us to work like that i.e. we should have different environment to work. We should be able to release it in these environments to be satisfied before ruining the actual production processes.

20. Audits of Rule Execution: The user should be given option of creating the audit trail of execution of rules. The audit trail should have details like user, application. The system should allow user to view this information in the form of reports.