Controller


Our final control flow pattern illustrates a situation in which external stimulus is the sole determinant of when and how often a child activity is executed. The Controller activity shown in Listing B.5 creates a WF program queue and listens for commands that tell it what child activities to execute. The Controller is completely passive and relies entirely on external code to drive the execution of its child activities.

Listing B.5. Controller Activity

 using System; using System.Workflow.ComponentModel; using System.Workflow.Runtime; namespace EssentialWF.Activities {   public class Controller : CompositeActivity   {     protected override void Initialize(       IServiceProvider provider)     {     WorkflowQueuingService qService = provider.GetService(       typeof(WorkflowQueuingService))         as WorkflowQueuingService;     WorkflowQueue queue = qService.CreateWorkflowQueue(       this.Name, false);     }     protected override void Uninitialize(       IServiceProvider provider)     {       WorkflowQueuingService qService = provider.GetService(         typeof(WorkflowQueuingService))           as WorkflowQueuingService;       qService.DeleteWorkflowQueue(this.Name);     }     protected override ActivityExecutionStatus Execute(       ActivityExecutionContext context)     {       WorkflowQueuingService qService = context.GetService(         typeof(WorkflowQueuingService))           as WorkflowQueuingService;       WorkflowQueue queue = qService.GetWorkflowQueue(this.Name);       queue.QueueItemAvailable += this.ResumeAt;       return ActivityExecutionStatus.Executing;     }     void ResumeAt(object sender, QueueEventArgs e)     {       ActivityExecutionContext context =         sender as ActivityExecutionContext;       WorkflowQueuingService qService = context.GetService(         typeof(WorkflowQueuingService))           as WorkflowQueuingService;       WorkflowQueue queue = qService.GetWorkflowQueue(this.Name);       string s = (string)queue.Dequeue();       if (s != null)       {         if (s.StartsWith("execute"))         {           // command will be something like 'execute w1'           string[] tokens = s.Split(new char[] { ' ' });           string name = tokens[1];           Activity child = GetChildActivity(name);           if (child != null)           {             ActivityExecutionContextManager manager =               context.ExecutionContextManager;             ActivityExecutionContext c =               manager.CreateExecutionContext(child);             c.Activity.Closed += this.ContinueAt;             c.ExecuteActivity(c.Activity);           }         }       }     }     void ContinueAt(object sender,       ActivityExecutionStatusChangedEventArgs e)     {       ActivityExecutionContext context =         sender as ActivityExecutionContext;       ActivityExecutionContextManager manager =         context.ExecutionContextManager;       ActivityExecutionContext c =         manager.GetExecutionContext(e.Activity);       manager.CompleteExecutionContext(c, false);     }     private Activity GetChildActivity(string name)     {       foreach (Activity child in EnabledActivities)       {         if (child.Name.Equals(name))           return child;       }       return null;     }     // Cancellation logic     ...   } } 


The Controller activity receives commands (as strings) from its WF program queue. When it receives a command, such as "execute w1", it will find the specified child activity ("w1" in this example) and schedule it for execution. It would be simple to extend this logic so that the Controller can also handle commands for canceling child activities and reporting its own completion.

For example, consider the following program:

 <Controller x:Name="controller1" xmlns="http://EssentialWF/Activities xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">   <WriteLine Text="1" x:Name="w1" />   <WriteLine Text="2" x:Name="w2" />   <WriteLine Text="3" x:Name="w3" /> </Controller> 


The child activities can be executed in any order, according to the commands that are sent to the Controller via its WF program queue:

  WorkflowInstance instance = runtime.CreateWorkflow(...);  string queueName = instance.GetWorkflowDefinition().Name; ... instance.EnqueueItem(queueName, "execute w1", null, null); instance.EnqueueItem(queueName, "execute w3", null, null); instance.EnqueueItem(queueName, "execute w3", null, null); instance.EnqueueItem(queueName, "execute w2", null, null); 


The preceding external code will drive the WF program to produce the following output:

 1 3 3 2 


In this example, we are only using WriteLine activities, but the Controller supports child activities of any type. Child activities can execute in an interleaved manner (for example, if a second "execute" command arrives before the first child activity that is executed completes). In fact, the same child activity can be executed simultaneously (in a manner similar to that of InterleavedForEach).

The Controller activity also supports dynamic modifications, so it is easy to add child activities to a Controller within a running WF program instance. This makes it possible to write a WF program that is just an empty Controller activity. Child activities can be dynamically added to the Controller, and then scheduled for execution one or more times via the "execute" command, allowing the user to effectively write the program as it is running.




Essential Windows Workflow Foundation
Essential Windows Workflow Foundation
ISBN: 0321399838
EAN: 2147483647
Year: 2006
Pages: 97

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net