Creating the Sample Application
With the database prepared, let’s now build a very simple workflow application to see how a workflow instance can be persisted.
Using Visual Studio 2005, create a new Sequential Workflow Console Application and name it C:\PersistenceWorkflow.
In the Design View of WorkFlow1.vb, drag-and-drop the following Workflow activities (see also Figure 6):
Set the properties of the activities as shown in Table 1.
Table 1. Workflow Activities and their Corresponding Properties.
Activity
|
Property
|
Value
|
codeActivity1 |
ExecuteCode |
Code1 |
delayActivity1 |
TimeoutDuration |
00:00:05 |
codeActivity2 |
ExecuteCode |
Code2 |
In the Code View of Workflow1.vb, code the following:
Private Sub Code1( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs)
Console.WriteLine("In CodeActivity1: " & Now.ToString)
End Sub
Private Sub Code2( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs)
Console.WriteLine("In CodeActivity2: " & Now.ToString)
End Sub
In Module1.vb, there is already some code written for you to host the workflow. Add to it the following code shown in bold typeface:
Shared Sub Main()
Using workflowRuntime As New WorkflowRuntime()
AddHandler workflowRuntime.WorkflowCompleted, _
AddressOf OnWorkflowCompleted
AddHandler workflowRuntime.WorkflowTerminated, _
AddressOf OnWorkflowTerminated
AddHandler workflowRuntime.WorkflowIdled, _
AddressOf OnWorkflowIdled
AddHandler workflowRuntime.WorkflowPersisted, _
AddressOf OnWorkflowPersisted
AddHandler workflowRuntime.WorkflowUnloaded, _
AddressOf OnWorkflowUnloaded
AddHandler workflowRuntime.WorkflowLoaded, _
AddressOf OnWorkflowLoaded
workflowRuntime.AddService( _
New SqlWorkflowPersistenceService( _
"Initial Catalog=SqlPersistenceService;" & _
"Data Source=.\SQLEXPRESS;Integrated " & _
"Security=SSPI;"))
Dim workflowInstance As WorkflowInstance
workflowInstance = _
workflowRuntime.CreateWorkflow(GetType(Workflow1))
workflowInstance.Start()
WaitHandle.WaitOne()
Console.ReadLine()
End Using
End Sub
Essentially, you are adding the other event handlers for the workflow so that you'll be able to tell when the workflow is idled, persisted, loaded, and unloaded. In addition, this code adds a new SqlWorkflowPersistenceService service to the workflow. This will allow an instance of your workflow to be persisted to a SQL database when persistence should occur. The connection string to the database is passed as a parameter to this service.

Figure 6. Workflow: Design the workflow with two activities: code and delay.
|
|

Figure 7. You can learn a lot about what series of events occurred when the application runs by observing the messages printed when the workflow executes. |
I also added a Console.ReadLine() statement so that the console window will not be closed immediately after the workflow finishes execution. This will allow you to observe the values printed on the screen.
In the OnWorkflowCompleted and OnWorkflowTerminated event handlers, add the two WriteLine() statements as shown in bold typeface below:
Shared Sub OnWorkflowCompleted( _
ByVal sender As Object, _
ByVal e As WorkflowCompletedEventArgs)
Console.WriteLine("Workflow completed.")
WaitHandle.Set()
End Sub
Shared Sub OnWorkflowTerminated( _
ByVal sender As Object, _
ByVal e As WorkflowTerminatedEventArgs)
Console.WriteLine("Workflow terminated.")
Console.WriteLine(e.Exception.Message)
WaitHandle.Set()
End Sub
Add the following event handlers:
Shared Sub OnWorkflowIdled( _
ByVal sender As Object, _
ByVal e As WorkflowEventArgs)
Console.WriteLine("Workflow idle.")
ThreadPool.QueueUserWorkItem( _
New WaitCallback(AddressOf UnloadInstance), _
e.WorkflowInstance)
End Sub
The OnWorkflowIdled event is invoked when a workflow goes into an idle state, such as when executing the Delay activity. For thread safety, you need to queue the events raised back from a workflow on the ThreadPool class.
The next event handler is the OnWorkFlowPersisted event:
Shared Sub OnWorkflowPersisted( _
ByVal sender As Object, _
ByVal e As WorkflowEventArgs)
Console.WriteLine("Workflow persisted.")
End Sub
This event handler is invoked when the workflow is persisted. It implicitly calls the
SaveWorkflowInstanceState method of the SqlPersistenceService class to save the workflow instance.
Once the workflow instance is persisted, the OnWorkflowUnloaded event is fired:
Shared Sub OnWorkflowUnloaded( _
ByVal sender As Object, _
ByVal e As WorkflowEventArgs)
Console.WriteLine("Workflow unloaded.")
End Sub
This event is invoked when the workflow is unloaded from memory. This is performed by the
UnloadInstance() method, which was referenced earlier in the OnWorkflowIdled event. The main purpose of the
UnloadInstance() method is to perform the unloading of workflow instance.
Shared Sub UnloadInstance( _
ByVal workflowInstance As Object)
CType(workflowInstance, WorkflowInstance).TryUnload()
End Sub
When a workflow instance is to be resumed, it must be loaded back into memory. This is performed by the OnWorkflowLoaded event. It implicitly calls the LoadWorkflowInstanceState method of the SqlPersistenceService class to load the saved workflow instance into memory:
Shared Sub OnWorkflowLoaded( _
ByVal sender As Object, _
ByVal e As WorkflowEventArgs)
Console.WriteLine("Workflow loaded.")
End Sub
Press F5 to test the application.
Figure 7 shows the output observed. Notice that there is a delay of five seconds between the first and second Code activities. Also note the series of events that were fired when the workflow instance was persisted to the SQL database and then loaded back into memory.