State Machine Workflows in Windows Workflow Foundation

来源:互联网 发布:matlab 约束最优化 编辑:程序博客网 时间:2024/05/02 00:03

基于windows workflow 的状态机工作流

 

 概要

我们可以实现一个基于 windows worklow 的以事件状态为核心的工作流。这篇文章用一个简单的 case 实例来说明如何开发一个状态机(state machine)工作流。并利用 case 来解决问题。

The solution is available for download . The code was developed for .NET Framework 3.0 June 2006 CTP.

Downloads:
  • Source Code (19,0 KB)
Contents:
  • What is a State Machine Workflow?
  • The case
  • The architecture of the solution
  • Developing States
  • Conditions
  • Declarative Rule Conditions
  • Selecting an interface for the HandleExternalEventActivity
  • The HandleExternalActivity
  • Global Events
  • The Workflow
  • Developing the Class
  • Inheriting from ExternalDataEventArgs
  • Hosting workflows in an application
  • Passing parameters to a Workflow
  • CurrentStateName
  • Performing a while loop
  • Invoking a Web service
  • Fault Handlers
  • Conditional Behavior
  • Dynamically Updating rules
  • About the author

什么是状态机工作流?

A workflow is a defined process consisting out of several steps which implement the needed behavior.

基本上分为两种工作流: 顺序工作流 and 状态工作流.

顺序工作流 的工作流程是通过 case and loops 事先定义好的,工作的流程在这个工作流的运行过程中是不可能发生改变的,也就是说是不可控制的。
状态工作流 的工作过程不是预先定义好的。它是以事件驱动的,当事件激活或事件的状态发生改变时,状态工作流的工作流程也会发生相应的改变。是可以控制的, 所以当需要与用户交互时状态工作流有更多的灵活性
When new requirements come up, state machines are more flexible to add features. Mostly it’s a matter of adding another state. The state machine is not in control of the flow, but only controls the set of choices which can be issued by another application.

As state machines are a rather unknown concept, we’re going to build a simple but recognizable situation in this article.


The case

In this article we are focusing on a small case with some requirements which allows us to look at different concepts in state machines. The case is not a typical implementation we see in IT but it’s a recognizable scenario to learn how state machines work.

Let’s say we’re developing a state machine workflow for a TV. The state of the TV changes during an evening watching it. Our TV has following states:

  • TV is Off (TVIsOff)

  • TV is On, and we’re browsing the channels to seek an interesting show (TVisOnBrowsingChannels)

  • We browsed all channels and found no interesting show (TVAllChannelsBoring)

  • We found an interesting show and we’re watching it. (TVWatchingInterestingShow)

  • Furthermore, the TV has the additional functionality to receive breaking news items. These news flashes can interupt the show being broadcasted. (TVWatchingBreakingNews)

  • The BroadcastService could not be available, so we cannot watch anything (TVBroadcastServiceNotAvailable)

The workflow allows to move the TV through the different states by events (some internal, but mostly external), by decisions made in the logic of the workflow and by output of requests the workflow makes to services outside the workflow.

The most important concept of state machine workflows is that they are event driven. By reacting to events we can put the workflow in another state which results in another behavior. So the question is ‘what are the possible events that can happen with the TV?

  • We turn it on.

  • After turning it on we start browsing the channels (there are 25 channels and we browse them sequentially).

  • When a channel with an interesting show is found we watch it.

  • When all 25 channels are boring we turn the TV off.

  • When browsing for a channel and the broadcast service is not available we wait an amount of time to check again. This is done without turning the TV off.

  • When we’re watching an interesting show, after an amount of time we get tired an turn the TV off.

  • Breaking news comes in and we decide to see it.

  • Breaking news comes in and we decide not to see it.

  • Breaking news ends and we can watch the show again.

  • Phone rings, we need to answer so we turn off the TV.

As you see the events are either decisions made by the workflow or by external events. Breaking news and phones ringing are for instance external events. Retrying the channel after the broadcast service was not available is a desicion of the workflow itself. This combination is a typical situation for state machine workflows. So the workflow has to have some handlers to react to events by changing the state and must also be capable to changing the state by itself. Internal changes will mostly be triggered by a timer elapsing or by desicions which can be configured dynamically.


The architecture of the solution

We need a class which represents the TV (or more common our Business Logic) and a workflow who can use this class to perform the needed behavior. The branches in the workflow can be based on rules which are stored outside the workflow. We simulate the broadcasting service in the case by using a web service.





The host application starts the TVClass and adds it to the workflow. After the workflow is started the application calls methods on the TVClass. These methods raise events in the workflow so it can change its state and perform the needed requirements by running code or other activities in the workflow for the desired state. In one of the states, the workflow uses a web service to select a channel. The web service responds with a boring or interesting show. The workflow has to be aware that the web service might be unreachable and has to solve the raised exception.


Developing States

The workflow designer in Visual Studio allows us to create the workflow containing the states and activities in a visual way. We can drop shapes from the toolbox in the designer which generates code. A shape represents an activity which can be configured by properties and can contain more activities or link to a new designer with sub activities.

3 types of activities are possible in a state:

  • StateInitializationActivity : Executed when the workflow enters the state.

  • StateFinalizationActivity : Executed when the workflow leaves the state

  • EventDrivenActivity : A construct where the workflow can listen to an event.





在一个工作流里只能添加一个 StateInitializationActivity 和 StateFinalizationActivity 但是可以有多个EventDrivenActivity ,所以工作流在同一时间可以监听多个事件. Each of these activities can be made up by other activities in a sequential order.

The purpose of the EventDrivenActivity is to execute the desired behavior and can contain logic to decide whether to move to another state. Because an EventDrivenActivity is only performed when something is triggered the first activity in the EventDrivenActivity can either be:

  • a DelayActivity

  • a HandleExternalEventActivity









A DelayActivity waits for a specified amount of time. When the time elapses the activities following the DelayActivity are performed. In our case when the workflow is in the state of watching an interesting show we could have a DelayActivity to code the fact we’re getting tired of watching after a while and turn the TV off.

A HandleExternalEventActivity waits for an external event to happen. The activity has to be configured with an InterfaceType. This is an interface in the project or referenced assemblies with events which use ExternalDataEventArgs as arguments. The events are raised by the class containing the business logic (in our case a class which represents the TV).

This interface describes the communication between the class and the workflow. The class informs the workflow by raising events. The workflow can capture the data send in the event in its local member variables. These are available throughout the workflow.


Conditions


In workflows there’s the need for conditions. The condition can be used in a branch or act as a condition to end while loops. These conditions can be code in Visual Basic or C# resulting in “true” or “false”. In this case the condition is compiled into the workflow. Beside code conditions, Windows Workflow Foundation allows us to use declarative rules conditions. These rules are kept in external files and can be changed dynamically. By allowing a running instance of a workflow to be given another set of rules we can separate the rules from the compiled workflow.

So there are 2 types of conditions:

  • Code Condition : The logic to calculate whether a condition returns true or false is programmed in the workflow. Because of this, it is compiled into the assembly.

  • Declarative Rule Condition : The logic to calculate whether a condition returns true or false is described declaratively without code. This logic is contained in external .rules files.


Declarative Rule Conditions

In this case we will use declarative rule conditions to define whether the breaking news is allowed to interrupt the show we’re watching. Let’s imagine we can configure our TV to only switch to the breaking news when the news has a certain priority. Assuming the breaking news sends this priority together with the news a rule could decide whether to show the news or not. The rule can be different for each workflow instance. I’ll discuss changing the rules later.

An external rule file has the extension .rule. In this file the rules are contained in XML notation.

Let’s say that the host application calls methods on the TVClass to indicate that there’s breaking news and adds a parameter to the event with the severity (a number between 0 and 100) of the news item. The workflow changes the state from watching the show (TVWatchingInterestingShow) to watching the breaking news (TVWatchingBreakingNews) based on this severity. When the severity is above a certain level the show is interrupted. The decision could be dynamic. One evening we’re only interested in breaking news with a severity above 50. The other evening we’re interested in all news items.

The fact that decisions can change outside the compiled workflow is a common situation in lots of workflow scenarios.

So we have 2 .rules files:

Code snippet for SeeBreakingNewsAlways.rules

<RuleExpressionCondition Name="CheckSevevirtyOfBreakingNews">  <RuleExpressionCondition.Expression>    ...    <ns0:CodeBinaryOperatorExpression Operator="GreaterThan">      ...      <ns0:CodePrimitiveExpression.Value>        <ns1:Int32>0</ns1:Int32>      </ns0:CodePrimitiveExpression.Value>      ...    </RuleExpressionCondition.Expression></RuleExpressionCondition>      


Code snippet for SeeBreakingNewsFromSeverity50.rules

<RuleExpressionCondition Name="CheckSevevirtyOfBreakingNews">  <RuleExpressionCondition.Expression>    ...    <ns0:CodeBinaryOperatorExpression Operator="GreaterThan">      ...      <ns0:CodePrimitiveExpression.Value>        <ns1:Int32>50</ns1:Int32>      </ns0:CodePrimitiveExpression.Value>      ...    </RuleExpressionCondition.Expression></RuleExpressionCondition>      

Selecting an interface for the HandleExternalEventActivity

The HandleExternalEventActivity is like an event. It’s fired when a predefined action is triggered. This is done by the TVClass. The activity has to know which of the methods in the class is causing the external activity. This must be done by first selecting the interface which contains the methods. So the ITVEvents interface acts as the contract between the class and the workflow.

The interface is needed as the base of the contract. By separating the actual code from this contract we can develop the workflow by the ‘design by contract’ principle. The workflow is tied to an interface not to the actual class. The class which contains the logic is replaceable (sometimes called pluggable) and can evolve over time without the need to adjust the workflow. The workflow just expects an object which implements this interface. It’s a level of abstraction. Once a workflow is build on top of the interface and is deployed, the logic behind the class can change as long as the contract is respected. Therefore a solid analysis of the requirements is needed to define the possible events in the interface.

In our case it’s clear that following events are needed: TurnOn, TurnOff, BreakingNewsComesIn, BreakingNewsDone and PhoneCall. The code for the interface looks like this:

using System;using System.Collections.Generic;using System.Text;using System.Workflow.Activities;namespace TVClass {    [ExternalDataExchange]    public interface ITVEvents {      event EventHandler<ExternalDataEventArgs> TurnOn;      event EventHandler<ExternalDataEventArgs> TurnOff;      event EventHandler<BreakingNewsEventArgs> BreakingNewsComesIn;      event EventHandler<ExternalDataEventArgs> BreakingNewsDone;      event EventHandler<ExternalDataEventArgs> PhoneCall;    }}      

The HandleExternalActivity

The HandleExternalActivity has to be configured with the needed interface by selecting the type name of the interface. This type can reside in the assembly of the workflow or in a referenced assembly.





After selecting the type we can set the EventName. This is the method we’re expecting to happen. All the valid events in the interfaceType are available in the EventName dropdown.





By now the workflow knows to which event it has to listen. When the event is fired by a class implementing the interface the workflow performs the activities following the HandleExternalActivity.

Next we insert the SetStateActivity in the workflow after the HandleExternalEventActivity and we set the state to which the workflow must transform to. This is done in the TargetStateName dropdown which contains all possible states in the workflow.





The workflow now looks like this:





The sequential flow of the EventDrivenActivity has two shapes in a sequential order. The first instructs the workflow to wait for an external event which is defined by the interface and the event name within it. The seconds tells the workflow to change its state to another state.

In our case when the state is TVIsOff we wait for the event TurnOn raised by the TVClass. When the event is received we switch to the TVIsOnBrowsingChannels. When entering this new state the workflows performs the code (again defined by shapes in a sequential way) in the StateInitializationActivity and then waits on the events defined in EventDrivenActivities or times out on a DelayActivity shape to decide to change its state by itself.

Switching back to the main flow we see that the states are connected now. The overall diagram shows a arrowed line reflecting the possible change between states.






Global Events

A change in the state should not always be triggered from within an EventDrivenActivity in a state. You can create a global event which is always available and isn’t tied to a particular state. In our case we can implement the fact the phone could ring at any moment regardless of the state of our TV. This is done by adding an EventDrivenActivity at the global workflow view and configuring it with an interface type, EventName and a SetStateActivity just like it was done before.


The Workflow

When all requirements are developed the workflow looks like this. It’s a good practice to align the state shapes in a manner that all possible changes are visible.





One state of the workflow has to be assigned as the initial workflow so when starting the workflow the runtime knows which code in StateInitializationActivity it has to execute and for which activities it has to listen. This initial state is indicated by the green icon in the left corner of the state shape.


Developing the Class

As said before, the used class has to implement the interface. So the workflow can be sure the events to which it reacts are available in the object on which the workflow is performing. So the TVClass has the same public events as described in the interface. Besides the public events the class contains public functions that can be used by the host application (or another external application). When the host application issues a method, this method checks if the event handler is valid by testing the reference for null. If it’s not null we’re sure that event is cached by a HandelExternalEventActivity in the workflow so it can be raised.

This leads us to the common pattern in implementing state workflows. Here we see that changes in the states are triggered by external events which are - on their turn - triggered by a public method in the class called by an external application. In most cases this external application is the host.

Application performs method on an object -> the object raises the event into the workflow -> workflow can react by changing its state.

It’s important that the class is tagged as [serializable] allowing the workflow runtime to serialize the class to a persistence store like SQL Server.

In our case the host will call methods on the TV Class.

These are: HostSays_TurnOn, HostSays_BreakingNewsComesIn, HostSays_BreakingNewsDone, HostSays_PhoneCall, HostSays_TurnOff

The code for the TV Class:

using System;using System.Collections.Generic;using System.Text;using System.Workflow.Activities;namespace TVClass {    [Serializable]    public class TVClass : ITVEvents {        public event EventHandler<ExternalDataEventArgs> TurnOn;        public event EventHandler<ExternalDataEventArgs> TurnOff;        public event EventHandler<BreakingNewsEventArgs> BreakingNewsComesIn;        public event EventHandler<ExternalDataEventArgs> BreakingNewsDone;        public event EventHandler<ExternalDataEventArgs> PhoneCall;        public void HostSays_TurnOn(Guid InstanceID) {            if (this.TurnOn != null) {                this.TurnOn(this, new System.Workflow.Activities.ExternalDataEventArgs(InstanceID));            }        }        public void HostSays_BreakingNewsComesIn        (Guid InstanceID, String Title, String Description, int Severity) {            if (this.BreakingNewsComesIn != null) {                this.BreakingNewsComesIn(this,                new BreakingNewsEventArgs(InstanceID, Title, Description, Severity));            }        }        public void HostSays_BreakingNewsDone(Guid InstanceID) {            if (this.BreakingNewsDone != null) {                this.BreakingNewsDone(this,                new System.Workflow.Activities.ExternalDataEventArgs(InstanceID));            }        }        public void HostSays_PhoneCall(Guid InstanceID) {            if (this.PhoneCall != null) {                this.PhoneCall(this,                new System.Workflow.Activities.ExternalDataEventArgs(InstanceID));            }        }        public void HostSays_TurnOff(Guid InstanceID) {            if (this.TurnOff != null) {                this.TurnOff(this, new System.Workflow.Activities.ExternalDataEventArgs(InstanceID));            }        }    }}

The class diagram for this class gives us a good overview of the methods and events in both TVClass and ITVEvents interface.









Each of the methods available to the host application has a parameter called InstanceID which is a GUID. The host is responsible to provide this GUID to make multiple instances possible. Each instance needs the unique GUID to isolate it from the other instances so they can be in other states. This GUID can also be used by the host application to reload workflows that were persisted. The GUID acts as the identity of the running or persisted instance of the workflow.

The events are given a generic parameter called ExternalDataEventArgs. This allows sending parameters to the workflow which can be passed through to the HandleExternalEventActivity. In most cases the ExternalDataEventArgs are sufficient unless you want to send some domain specific data to the workflow.

In our case, we want to pass the severity of a news item as extra data for the BreakingNewsComesIn event.

That’s why the BreakingNewsComesIn event handler has BreakingNewsEventArgs instead of ExternalDataEventArgs as the argument for the event. This argument is of a type which inherits from ExternalDataEventArgs


Inheriting from ExternalDataEventArgs

So in this case we need to pass additional data with the event to the workflow. We need a class which inherits from ExternalDataEventArgs and has the properties to contain the extra data. In our case this additional data contains the severity level which is used by the external rules to decide whether to change states.

The method in the TVClass (which is called by the host) raises the events with a new instantiation of this class as the parameter. The ExternalEventActivity handler receives this without problem because the arguments are inherited form the expected class.

Code for this BreakingNewsEventArgs type:

public class BreakingNewsEventArgs : System.Workflow.Activities.ExternalDataEventArgs {    private string _Title;    public string Title {        get { return _Title; }        set { _Title = value; }    }    private string _Description;    public string Description {        get { return _Description; }        set { _Description = value; }    }    private int _Severity;    public int Severity {        get { return _Severity; }        set { _Severity = value; }    }    public BreakingNewsEventArgs(Guid InstanceID, String Title, String Description,    int Severity)        : base(InstanceID) {        this.Title = Title;        this.Description = Description;        this.Severity = Severity;    }}




The host application sends the data to the method in the TVClass. The method wraps the data up in the BreakingNewsEventArgs and passes it through to the workflow instance.

myTV.HostSays_BreakingNewsComesIn(  TVinstanceID,"NEWSFLASH 25", "Something not so important happened", 25);
public void HostSays_BreakingNewsComesIn (Guid InstanceID, String Title, String Description, int Severity) {    if (this.BreakingNewsComesIn != null) {        this.BreakingNewsComesIn(this,        new BreakingNewsEventArgs(InstanceID, Title, Description, Severity));    }}

Hosting workflows in an application

The workflow has to be hosted inside an application. This can be a console application, Windows application, Windows service... Other hosts can be SharePoint Portal Server 2007 or Windows SharePoint Services 3.0. Besides the workflow the application also needs to instantiate the class. The instantiated class has to be added to the workflow runtime as a service. After creating the workflow in the runtime the workflow instance has to be started.

To start we need to setup some variables: a variable containing the instance of our workflow. In this case we’re instantiating only one instance. When more instances are needed this would typically be an array or a collection.

WorkflowInstance MyWorkflowInstance;

A variable for the class is needed.

TVClass.TVClass myTV;

We also need a GUID to inform the workflow about the instance we’re targeting.

Guid TVinstanceID;

At last we need a reference to the workflowRuntime.

WorkflowRuntime workflowRuntime;

The TVClass and a WorflowRuntime are instantiated. The runtime needs a DataService which in its turn has the TV instantiation as its service. The DataService is of the type ExternalDataExchangeService which resides in the System.Workflow.Activities namespace.

After the WorkflowRuntime has got the service, the GUID is created and the workflow is started by the CreateWorkflow method. This method must receive the type of the workflow class and the GUID referring to the instance. This returns the WorkflowInstance.

Finally the instance of the workflow is started. This is the signal for the workflow runtime to execute the logic in the InitialStateActivity and start listening for the EventDrivenActivities defined in the initial state.

myTV = new TVClass.TVClass();'workflowRuntime = new WorkflowRuntime();System.Workflow.Activities.ExternalDataExchangeService dataService =  new System.Workflow.Activities.ExternalDataExchangeService();workflowRuntime.AddService(dataService);dataService.AddService(myTV);TVinstanceID = Guid.NewGuid();MyWorkflowInstance = workflowRuntime.CreateWorkflow(  typeof(TVState machineWorkflowLibrary.WatchingTVWorkflow),  null,  TVinstanceID);MyWorkflowInstance.Start();      

Passing parameters to a Workflow

Typically a workflow needs to have some data available when started. This data can be provided by the host application. In our case we pass the channelID we want to try first when browsing the channels to the workflow.

Passing parameters is done by a generic Dictionary collection. This collection holds the name of the property as the key and the object you want to pass as the value. You configure which property in the workflow has to get what value by adding the name of the property and the value (as an object) to the dictionary. Adding this dictionary as a parameter to CreateWorkflow method of the WorkflowRuntime makes the data available in the workflow.

In the workflow the parameters in the collection are received in the according public properties.

Dictionary<string,object> Params = new Dictionary<string,object>();Params.Add("TryChannelID", 1);MyWorkflowInstance = workflowRuntime.CreateWorkflow(  typeof(TVState machineWorkflowLibrary.WatchingTVWorkflow),  Params,  TVinstanceID);      

The propertie in the workflow :

private int _TryChannelID;public int TryChannelID {  get { return _TryChannelID; }  set { _TryChannelID = value; }}      

CurrentStateName

The code running in the workflow is aware of its current state. This is useful for debugging and troubleshooting. By adding a CodeActivity shape to the StateInitialization event we can write the name of the state to the console or output window of Visual Studio.

Console.WriteLine("STATE :" + this.CurrentStateName);

Performing a while loop

An EventDrivenActivity is a sequential workflow which can contain loops and/or branches. This can be done by adding activities (the WhileActivity for instance) from the toolbox and configuring it.






A while loop needs a condition which decides to repeat the loop or not. This decision can be a code condition or a declarative rule condition. The code condition is made out of a void method which has a ConditionalEventArgs parameter. This object has a property Result to indicate the outcome of the condition.

private void CheckForChannels(object sender, ConditionalEventArgs e) {    if (!ChannelIsInteresting) {        Console.WriteLine("...Trying Channel {0}", TryChannelID);    }    if ((TryChannelID < 25) && (!ChannelIsInteresting)) {        e.Result = true;    } else {        e.Result = false;    }    TryChannelID++;}      

Invoking a Web service

In a SOA world we need to be able to call web services in the workflow of course. Invoking a web service can be implemented by adding an InvokeWebserviceActivity. We configure the shape with the URL for the .asmx file. By doing this, the WSDL file is downloaded and a class containing the proxy is generated. Also a list with the available methods is made up so the needed method can be selected. Parameters of the method and the variable containing the results are now dynamic properties of the shape depending on the method.

In our case the method name is ReceiveAShow with a parameter ChannelID. The method returns a boolean value which is received in the variable ChannelIsInteresting. These variables need to be declared in the workflow code.

private int _ChannelID;public int ChannelID {  get { return _ChannelID; }  set { _ChannelID = value; }}private System.Boolean _IsInteresting;public System.Boolean IsInteresting {  get { return _IsInteresting; }  set { _IsInteresting = value; }}      

These variables are bound to the Web service in the InvokeWebservice Shape.





We simulate whether an interesting show or a boring show is send by a returning true or false based on a random number. The code of the web service:

[WebMethod]public System.Boolean ReceiveAShow(int ChannelID) {    Random r = new System.Random();    if (r.Next(100) > 85) {        return (true);    } else {        return (false);    }}

Fault Handlers

The worfklow has to react to exceptions occurring or are thrown into the workflow. To configure Fault Handlers we have to switch to the Fault Handlers view of the activity where we expect the exception (like the While Activity).





The designer switches to the Fault Handlers view and the design surface looks like this:





Now we need to drop one or more FaultHandlers on the shape. This allows us to catch the exception and react to it by performing other activities. Each FaultHandler has its own set of activities. It’s possible to have a catch for more than one exception. This construct is comparable to the try … catch statements in .NET.

We can configure the FaultHandler by selecting the FaultType out of all possible .NET types deriving from System.Exception.





The exception object itself is copied into a member variable of the workflow and is available in the other activities. The code for this member is generated by clicking on Promote Bindable Properties.





The generated code looks like this:

public static DependencyProperty faultHandlerActivity1_Fault1Property  = DependencyProperty.Register("faultHandlerActivity1_Fault1",  typeof(System.Exception),  typeof(TVState.machineWorkflowLibrary.WatchingTVWorkflow));[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)][BrowsableAttribute(true)][CategoryAttribute("Misc")]public Exception faultHandlerActivity1_Fault1 {  get {    return ((System.Exception)    (base.GetValue(     TVState.machineWorkflowLibrary.WatchingTVWorkflow.faultHandlerActivity1_Fault1Property)));  }  set {    base.SetValue(    TVState.machineWorkflowLibrary.WatchingTVWorkflow.faultHandlerActivity1_Fault1Property,    value);  }}      

In our case we have to catch the exception that the web service could not be available. In the fault handler we set a variable in the workflow to tell it that the service wasn’t available. In the activities following the call to the service we check this variable.

So the result of the call to the service can be one of three situations:

  • Service wasn’t available, which ends the loop because of the exception.

  • Show is boring.

  • Show is interesting.


Conditional Behavior

To react to the three possible outcomes of the while loop we need an IfElseActivity which can contain multiple branches. Each individual branch (except the last one) needs a condition. If a condition is met, the activities in the branch are executed. Again the condition can be a code condition or a rule condition.










A rule condition is coded by the Rule Condition Editor which supports IntelliSense to easily select member variables of the workflow class.





These conditions are placed by Visual Studio in .rules files in the solution. The .rules files can be used to change conditional behavior dynamically by the host.


Dynamically Updating rules

Windows Workflow Foundation allows us to change the rules defined as declarative rules by loading the .rules files into the running instance. This is done by using the WorkflowChanges class. First the .rules files must be deserialized to a RuleDefinitions class from an XmlTextReader pointing to the filename of the .rules file.

Next an instantiation of a WorkflowChanges object needs to point to the definition of the running workflow instance. With this reference the value of the RuleDefinitionsProperty must be set to the new rule definitions. After this changes can be applied to the instance and immediately become active overruling the previous ones.

RuleDefinitions TheNewRuleDefinitions = new WorkflowMarkupSerializer().Deserialize(  new XmlTextReader(FileName)) as RuleDefinitions;WorkflowChanges TheWorkflowChanges = newWorkflowChanges(MyWorkflowInstance.GetWorkflowDefinition());TheWorkflowChanges.TransientWorkflow.SetValue(RuleDefinitions.RuleDefinitionsProperty, TheNewRuleDefinitions);MyWorkflowInstance.ApplyWorkflowChanges(TheWorkflowChanges);