Sunday, November 15, 2009

WF Local Services

In the previous discussion we discussed about such type of communication between workflow and hosting environment in which data is communicated between workflow and hosting environment. We discussed about flow of data in both directions.


There might also be scenarios in which one needs to use some behaviors (methods) of other. Workflow might need to execute some methods for its operation or it might wait for some events from the hosting application.


As we know that workflows are hosted in the workflow runtime by the hosting application. The hosting application might provide various services which could be used by the workflow for its operations. The generally used services provided by WF are persistence, transaction, tracking and scheduling services. For communication with workflow the hosting application might also provide local services. These are called local services because they are used for in-process communication between workflow and the hosting application.


Hosting Application:

Hosting application must provide the following:

  1. Contract of services in the form of interface. The contracts specified should be decorated with ExternalDataExchangeAttribute. This is available in System.Workflow.Activities namespace.

  1. Implementation of service contract as a concrete class.

  1. Adding service definition to the workflow runtime.


We create hosting application project. We create it as sequential console workflow project.


Step # 01: (Contract Definition)

We create the following contract:


[ExternalDataExchange]

interface CommunicationServiceContract

{

public string concatenateStrings(string firstString, string secondString);

event EventHandler<ArgumentsToWF> myEvent;

void fireActivityEvent(Guid instanceID);

}


Step # 02: (Implementing the service contract)

Now we create a class CommunicationService. This class implements the above defined contract:


class CommunicationService : CommunicationServiceContract

{

#region CommunicationServiceContract Members

string concatenateStrings(string firstString, string secondString)

{

return firstString + secondString;

}

public event EventHandler<ArgumentsToWF> myEvent;

public void fireActivityEvent(Guid instanceID)

{

this.myEvent(null, new ArgumentsToWF(instanceID, "From Host"));

}

#endregion

}


We also define an event argument which would be used when we will be discussing HandleExternalEventActivity activity.


[Serializable]

class ArgumentsToWF : ExternalDataEventArgs

{

public string myData { get; set; }

public ArgumentsToWF(Guid instanceID,string myData) : base(instanceID)

{

this.myData = myData;

}

}


Step # 03 (Adding Service to the workflow runtime)

Since we have created sequential workflow console project, we have some default implementation. We use the same Program.cs. Insert the following code in the Main method just before creation of the workflow instance:


ExternalDataExchangeService myService = new ExternalDataExchangeService();

workflowRuntime.AddService(myService);

CommunicationService localService = new CommunicationService();

myService.AddService(localService);


In the above code we have added an external data exchange service to the workflow runtime. We have specified our custom CommunicationService as the expected service.


Use of Local Service by Workflow:

There are basically two activities provided by WF for these purpose. These services are as follows:

  1. CallExternalMethod: When workflow needs to call some method of the local service.

  1. HandleExternalEvent: When workflow waits for an event from the hosting application.


From above discussion, you might have established that the difference between the two services is mainly about the direction of communication. In the first, workflow is using some behavior as specified in the hosting application. In other, workflow behavior is being used by hosting application. For understanding these activities, we would have to discuss them individually.


For any of the above two activities, the workflow must know the contract of the service in advance so that it could map the parameters and return values of these methods to its own properties. These properties can be defined on workflow or activities level. The greatest thing with these activities is that all the settings can be done using property window. Even the parameters and return values can be assigned to properties in the workflow. This makes it not only more simple but also more manageable.


CallExternalMethod Activity:

As discussed, this activity can help workflow use some implementation provided by the host application in terms of local service. These methods might be notifications of any operation to the host process.

We drop a CallExternalMethod activity to our workflow (Workflow1.cs).


Now we need to specify the service contract of the service that this contract would be using. Since we have created contract in the same project as the service for simplicity, we can select current project and selected CommunicationServiceContract. If we have added a reference dll containing the service contract, we could select service from the referenced assembly.



As soon as we select the contract, all the available methods of the contract are available for selection. We select our intended method concatenateString.



As soon as we select this method, the properties window is updated dynamically giving options for mapping the parameters and returning value from the selected method to properties in the workflow. We can also map them to any field / property of the activities contained by the workflow.



We create the following string properties in our workflow code in Workflow1 class:


public string wfFirstString { get; set; }

public string wfSecondString { get; set; }

public string wfReturnString { get; set; }


Now we generate handlers for the assignment and display of our properties.


private void Workflow1_Initialized(object sender, EventArgs e)

{

this.wfFirstString = "First ";

this.wfSecondString = "Second ";

this.wfReturnString = "";

}

private void Workflow1_Completed(object sender, EventArgs e)

{

Console.WriteLine(this.wfReturnString);

Console.ReadLine();

}


Now we run this application. As expected the console window is displayed. It has the following printed on the console:


“First Second ”


The workflow has used the service added to the runtime and called its concateStrings method which has concatenated the strings provided as arguments and returns the concatenated string.



There is one more option provided by this activity. Sometimes we have to do some housekeeping work before calling any external service. This may include some initializations or something else. This activity provides the facility to fire an event just before calling external service. We create the following method:


private void initializeServiceArguments(object sender, EventArgs e)

{

this.wfFirstString = "First ";

this.wfSecondString = "Second ";

this.wfReturnString = "";

}


We can specify this method to the MethodInvoking property of the activity as follows:



Now you might be wondering if we add two services implementing the same contract then how it would determine which implementation to use. The simplest answer to this question is that you don’t need to worry about this. This is because two implementation of the same service contract can not be added to runtime. So the runtime would not take the trouble of resolving the intended implementation details.



HandleExternalEvent Activity:

This is synchronous wait for an event which makes it a blocking call. As soon as the control reaches to this activity, it waits until the event is triggered by the hosting application. You might figure out the consequences if never an event is received. Since this is blocking, it keeps on waiting forever. There are techniques to avoid such a scenario but we would discuss about them later.


To understand this activity, let us add another sequential workflow to our project and name it Workflow2. Drag and drop HandleExternalEventActivity to the designer and name it handleExternalEvent. We set the properties of this activity as follows:



Here we have specified that this activity should wait for myEvent event from the hosting environment. We are also adding handler myHandlerAcitivity_Handler which would handle the event. Let us implement this event handler as follows:


private void myHandlerActivity_Handler(object sender, ExternalDataEventArgs e)

{

Console.WriteLine(((ArgumentsToWF)e).myData);

}


We are just printing the data in the event argument to the console.


Instead of adding the instance of Workflow1, we need to add the instance of Workflow2 which is now under discussion. Also we need to fire the event concerned.


WorkflowInstance instance =

workflowRuntime.CreateWorkflow(typeof(WFCommunication.Workflow2));

instance.Start();

localService.fireActivityEvent(instance.InstanceId);


Running the application this time should print the following on the console:


“From Host”


Performance Considerations:

We discussed two communication activities in this discussion i.e. CallExternalMethod and HandleExternalEvent activities. These activities use reflection to communicate with the provided services. As all of us know that using reflection has its performance implications which might not be acceptable for some scenarios. You would surely glad to know that there is another option.


You might be thinking if we are able to generate these activities based on the specific contract then workflow API would not have to resort to reflection to communicate with these services. If you think like that then you would be glad to know that your prayers are answered. Windows SDK provides a tool wca.exe. The name is basically the abbreviation of Windows Communication Activity generator. It generates specialized activities for us. The generated activities can be used in our workflows directly. These activities are created by specifying the contract information for the services which we intend to be used by the communication activities. Since these activities are created using the definition of contract that the services specified by handle or communicate activities would be implementing, there is no reflection is required to be used.


The following configurations can be set for WCA.exe. They are specified in WCA.exe.config file provided in the same folder as this tool. This file should look like this:


<?xml version="1.0" encoding="utf-8"?>

<configuration>

<runtime>

<generatePublisherEvidence enabled="false" />

</runtime>

</configuration>


As you can see, the only setting is for the publisher evidence. This is used to implement Code Access Security (CAS) policies. You can study further about this option from the following msdn link:


http://msdn.microsoft.com/en-us/library/bb629393.aspx


To generate the specialized activities, you have to have the contracts specified in an assembly (dll or exe). Out of the switches available, I want to discuss the following:


By using the different switches provided, we can specify the following options:


  1. Namespace: The namespace the generated activities should belong to. The default is the same namespace as the contract’s namespace (switch: /namespace).


  1. Output Directory: The output directory where we want code files to be created (switch: /out).


  1. Language: Currently we can generate activities in two possible languages. They are C# and VB. The default option is C# (switch: /language).


Ex: we have a dll containing the contract (interface) definition with the name WorkflowContracts.dll. We want to create the code files in c:\Activities folder. The language of the file should be vb. Then we need to execute the following command in command prompt after coming to the following folder:


wca.exe WorkflowContracts.dll /out:c:\Activities /language:cs

Wednesday, November 11, 2009

Passing Data IN and OUT of WF workflows

WF workflows are executed by hosting them in hosting applications using WF runtime. Sometimes we need data exchange between workflow and hosting application. Windows WF supports to communicate data in both direction. It is as simple as creating public properties in a class.


Passing Data to Workflow:

The data to workflow is sent using arguments when workflow object is instantiated. These arguments are sent using dictionary object. The workflow expects this as generic dictionary object with string keys and object values.


Dictionary<string, object> wfArguments = new Dictionary<string, object>();

wfArguments.Add("MyMessage", "From Hosting Application");

WorkflowInstance instance = workflowRuntime2.CreateWorkflow(typeof(

WFDynamicRuleUpdate.Workflow1), wfArguments);


We create a Sequential Workflow console application and name it as TestWorkflowParameters.


Designing Workflow:

Open Workflow1.cs in Workflow designer. Drag and drop a code activity to the designer window for the workflow.


Open workflow code and define a string property with the name of MyMessage.


public string MyMessage { get; set; }


Generate ExecuteCode handler for CodeActivity as follows:


private void codeActivity1_ExecuteCode(object sender, EventArgs e)

{

Console.WriteLine("Hello " + MyMessage);

MyMessage = "From Workflow";

}


Here we have updated the MyMessage argument. If we want we can get this value in the hosting application. To verify that we define the event handler for workflow completed event as follows:


Getting data from Workflow:

Workflow provides its public properties as output parameters. We can read the value of these properties using this OutputParameter collection. To discuss the most simple example let us define the WorkCompleted handler using lambda expression as follows:


workflowRuntime.WorkflowCompleted += (sender, arg) =>

{

waitHandle.Set();

Console.WriteLine("Hello " + arg.OutputParameters["MyMessage"].ToString());

};


When we run this application, it should show the following on the console:


Hello From Hosting Application

Hello From Workflow


In this discussion we discussed how easy it is to communicate between hosting application and workflow. We just have to define some public properties in the workflow and update their values. These values can be read by both workflow and hosting application.

Tuesday, November 10, 2009

Monitoring WF based workflows using Performance Monitoring Tool (Perfmon.exe)

Administrators need some way to monitor applications running in production. One way to provide such monitoring facility is to provide performance counters for the application. These counters can be viewed and logged using Performance Monitoring utility (Perfmon.exe) available with Windows.


Among the list of features provided with Windows WF, the easiest is using the Performance Counters. This is a limited option for viewing workflow runtime information. You can get details like the number of workflows being created and completed. This very limited option but can be of much use sometimes.


We divide this discussion in two parts. In first part we will be discussing about creating WF application which emits this information to be collected by a Performance monitoring application. Next we would be discussing how we can monitor these counters using available Windows tool (Perfmon.exe).


Creating WF workflow based application:


Create Sequential WF Console application Project:

Start Visual Studio and create a Sequential WF Console application. Name this project as WFPerformanceCounter.


Designing Workflow:

Open workflow1.cs in the WF designer. Drag and drop a Delay activity respectively. Take properties and specify a delay of 10 seconds (00:00:10) for delay activity.


Creating App.config file:

Add application configuration file to the project and name it as app.config. Delete all the xml data from this file and paste the following:


<?xml version="1.0"?>

<configuration>

<configSections>

<section name="CustomWorkflowRuntimeSettings" type="System.Workflow.Runtime.Configuration.WorkflowRuntimeSection, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

</configSections>

<CustomWorkflowRuntimeSettings Name="workflowRuntime" EnablePerformanceCounters="true" />

</configuration>


This enables the runtime from emitting performance counter information for any monitoring application. If you don’t want this information to be made available to the monitoring application, just set EnablePerformanceCounter attribute of CustomWorkflowRuntime element as “false”.


Updating Hosting Class:

By default, Visual studio creates Program.cs as the hosting class. This creates runtime object and initializes the workflow.


Add reference of System.Configuration assembly to the project. Specify System.Configuration namespace in Program.cs. This would provide System.Workflow.Runtime.Configuration.WorkflowRuntimeSection class to the application.


Specify the System.Configuration namespace in Program.cs.


using System.Configuration;


Update the initialization code of runtime object (Main method) as follows:


using (WorkflowRuntime workflowRuntime2 = new WorkflowRuntime( "CustomWorkflowRuntimeSettings"))

{…}


The above code specifies which configuration settings should be used for creating the runtime object.


Our application is now all set for providing performance counter information for the monitoring tool.


Note:

Remember that the default behavior of runtime is to emit counter information for collection. We can suppress this behavior by specifying this in the configuration file as discussed already.


Monitoring WF Performance Counters:

You can view the list of the available performance counters in Performance Monitor tool (Perfmon.exe). For all of those who are not available about this tool, this is a performance monitor tool available with windows. It lets you view the various system and application level performance counters. You can start it by typing “Perfmon.exe” on the Run command in Start menu.


There are following counters available for Windows WF.


  1. Workflows Aborted
  2. Workflows Aborted / sec
  3. Workflows Completed
  4. Workflows Completed / sec
  5. Workflows Created
  6. Workflows Created / sec
  7. Workflows Executing
  8. Workflows Idle / sec
  9. Workflows In Memory
  10. Workflows Loaded
  11. Workflows Loaded / sec
  12. Workflows Pending
  13. Workflows Persisted
  14. Workflows Persisted / sec
  15. Workflows Runnable
  16. Workflows Suspended
  17. Workflows Suspended / sec
  18. Workflows Terminated
  19. Workflows Terminated / sec
  20. Workflows Unloaded
  21. Workflows Unloaded / sec


Performance counters may be monitored for either any individual or all instances of WF runtime engines.

Select all the required counters and select “Add”. This would show the counters data on the System Monitor. For our purpose we add two counters:

  1. Workflow Created
  2. Workflow Completed

With all the above configurations, when we run our WFPerformanceCounter application the monitor should have following view:



Note that for this view you should have “View Current Activity” option selected with selecting Graph view.


Persisting Performance Counter Logs:

In addition to view online providence of counter information, we can also log them selectively using the same Performance monitor tool. The available destination of this information may be following:


  1. Text file (Comma or Tab delimited)
  2. Binary file
  3. Binary Circular file
  4. SQL Database


Binary Circular file starts overwriting old data from the beginning with the new data once the file reaches its maximum limit. This is useful when we are certain that we already have reviewed and audited old log information.

Persisting information using SQL Database could give us flexibility for developing tools for users so that they can view the information anytime. The better option is to create SQL Server Reporting Services based reports and reviewing these logs should be part of the overall audit review process.


No matter where you log this information i.e. file or database, you can always view it later on using the same tool (Performance Monitor Tool).



Monitoring Remotely:

As you all know, for using this Performance monitoring tool, you don’t have to be physically logged in to the machine. You can always view these counters manually from any machine on the network. The other option is that you can create logs in files or database and you can create reports based on these logs.


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