The WF RuntimeThe WF runtime is the execution environment for WF programs. In order to use the WF runtime, it must be hosted in a CLR application domain. This amounts to nothing more than instantiation of a System.Workflow.Runtime.WorkflowRuntime object. We refer to the application that instantiates the WF runtime as the host application. The WorkflowRuntime type is shown in Listing 2.13. Listing 2.13. WorkflowRuntime
We can run a WF program by invoking the WorkflowRuntime.CreateWorkflow method with a System.Xml.XmlReader that provides access to XAML. CreateWorkflow returns an object of type System.Workflow.Runtime.WorkflowInstance. A WorkflowInstance is a handle to a WF program instance. The WorkflowInstance type is shown in Listing 2.14. Listing 2.14. WorkflowInstance
Listing 2.15 is a console application that hosts the WF runtime. The console application uses the WF runtime to run one instance of the Open, Sesame program defined in Listing 2.11. Listing 2.15. Hosting the WF Runtime and Running a WF Program
The WF runtime must be told where to find the activity types that are used in the Open, Sesame XAML, and that is the job of the TypeProvider. When the Start method is called on the WorkflowInstance, the WF runtime runs the WF program instance asynchronously (on a different thread than the one on which Main is running). Other threading models are supported by the WF runtime, and are discussed in Chapter 5. When a ReadLine activity executes, it creates a WF program queue. When our console application (which is playing the role of a listener) reads a string from the console, it resumes the execution of the bookmark established by the ReadLine by enqueuing the string. The name of the WF program queue is the same name, "r1", that we gave to the ReadLine activity (per the execution logic of ReadLine). PassivationA reactive program like Open, Sesame is characterized by episodic executionit performs small bursts, or episodes, of work that are punctuated by relatively long idle periods spent waiting for stimulus from an external entity. During these idle periods, the WF program instance is inactive because it cannot make forward progress and the WF runtime can passivate itmove it from memory to persistent storage such as a database. The instance will be resumed, possibly in a different process on a different machine, when relevant stimulus arrives. This is illustrated in Figure 2.1.
Figure 2.1. WF program execution is episodic and distributed.
In order to support the passivation of WF program instances, the WF runtime must be configured to use a persistence service. WF includes a default persistence service, named SqlWorkflowPersistenceService, that uses SQL Server as the durable storage medium for WF program instances. As we will see in Chapter 5, it is possible to plug in a custom persistence service that uses whatever storage medium is appropriate for your solution. In order to use the SqlWorkflowPersistenceService in our solution, we of course must install SQL Server and create a database table that will be used to store passivated WF program instances.[1] To enable passivation, we can modify the console application logic to instantiate the SqlWorkflowPersistenceService and add it to the WF runtime:
using(WorkflowRuntime runtime = new WorkflowRuntime()) { SqlWorkflowPersistenceService persistenceService = new SqlWorkflowPersistenceService(...); runtime.AddService(persistenceService); ... } In order to illustrate the mechanics of passivation, we can write two different console applications. The first one, shown in Listing 2.16, begins the execution of an instance of the Open, Sesame program. Listing 2.16. Passivating a WF Program Instance
The WF program instance never completes because it is expecting to receive a string after it prints the key, and we do not provide it with any input. When the WorkflowInstance.Unload method is called,[2] the instance is passivated. Inspection of the SQL Server database table that holds passivated WF program instances will show us a row representing the idle Open, Sesame program instance.
In order to resume the passivated instance in another CLR application domain, we need to have some way of identifying the instance. That is precisely the purpose of the InstanceId property of WorkflowInstance. This globally unique identifier can be saved and then later passed as a parameter to the WorkflowRuntime.GetWorkflow method in order to obtain a fresh WorkflowInstance for the WF program instance carrying that identifier. This is exactly what we will do in a second console application, shown in Listing 2.17. Listing 2.17. Resuming a Passivated WF Program Instance
The passivated (bookmarked) WF program instance picks up where it left off, and writes its result to the console after we provide the second string. |