Manually Unloading and Loading the Workflow Instance
The previous example showed how a workflow runs and then is automatically persisted when it encounters the Delay activity. Besides this, you can also manually persist a workflow instance. To see how this is done, let's use a Windows application as the host in this next example.
Using Visual Studio 2005, create a new Sequential Workflow Library project and name it C:\WorkflowLibrary1.
Create the workflow in Workflow1.vb as shown in Figure 8. This is similar to the workflow created earlier, without the delayActivity activity.
In the code-behind 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)
System.Threading.Thread.Sleep(5000)
End Sub
Private Sub Code2( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs)
Console.WriteLine("In CodeActivity2: " & Now.ToString)
End Sub
Note that I have inserted a
Thread.Sleep() statement to simulate the activity taking a long time to process. In this case, the delay is five seconds.
Build the WorkflowLibrary1 project by right-clicking on its project name in Solution Explorer and then select Build.
Add a new Windows application project to the current solution. Name it C:\PersistenceWorkflowWindow. Add the System.Workflow.Runtime assembly to the project (right-click on project name and select Add Reference…). Also add the WorkflowLibrary1.dll that you just built to the project.
Add three Button controls to the default Form1 (see Figure 9). The three buttons are to start a workflow, unload a workflow from memory, and to load a workflow into memory.

Figure 8. This second workflow is the same as the earlier one without the Delay activity.
|
|

Figure 9. Populate the default Form1 with three Button controls. |
Switch to the code-behind of Form1 and import the following namespace:
Imports System.Workflow.Runtime
Imports System.Workflow.Runtime.Hosting
Imports System.Threading
Declare the following member variables:
Public Class Form1
Private WaitHandle As AutoResetEvent
Private workflowRuntime As New WorkflowRuntime()
Private workflowInstance As WorkflowInstance
In the Load event of the form, add the various event handlers to the WorkflowRuntime object and add the SqlPersistenceService service to it:
Private Sub Form1_Load( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
WaitHandle = New AutoResetEvent(False)
'---monitor workflow events---
AddHandler workflowRuntime.WorkflowCompleted, _
AddressOf OnWorkflowCompleted
AddHandler workflowRuntime.WorkflowTerminated, _
AddressOf OnWorkflowTerminated
AddHandler workflowRuntime.WorkflowUnloaded, _
AddressOf OnWorkflowUnloaded
AddHandler workflowRuntime.WorkflowPersisted, _
AddressOf OnWorkflowPersisted
AddHandler workflowRuntime.WorkflowIdled, _
AddressOf OnWorkflowIdled
AddHandler workflowRuntime.WorkflowLoaded, _
AddressOf OnWorkflowLoaded
workflowRuntime.AddService(( _
New SqlWorkflowPersistenceService( _
"Initial Catalog=SqlPersistenceService;" & _
"Data Source=.\SQLEXPRESS;Integrated" & _
" Security=SSPI;")))
End Sub
Define the event handlers as you did previously:
Private Sub OnWorkflowCompleted( _
ByVal sender As Object, _
ByVal e As WorkflowCompletedEventArgs)
Console.WriteLine("Workflow completed.")
WaitHandle.Set()
End Sub
Private Sub OnWorkflowTerminated( _
ByVal sender As Object, _
ByVal e As WorkflowTerminatedEventArgs)
Console.WriteLine("Workflow terminated.")
Console.WriteLine(e.Exception.Message)
WaitHandle.Set()
End Sub
Private Sub OnWorkflowUnloaded( _
ByVal sender As Object, _
ByVal e As WorkflowEventArgs)
Console.WriteLine("Workflow unloaded.")
End Sub
Private Sub OnWorkflowPersisted( _
ByVal sender As Object, _
ByVal e As WorkflowEventArgs)
Console.WriteLine("Workflow persisted.")
End Sub
Private Sub OnWorkflowIdled( _
ByVal sender As Object, _
ByVal e As WorkflowEventArgs)
Console.WriteLine("Workflow idle.")
ThreadPool.QueueUserWorkItem( _
New WaitCallback(AddressOf UnloadInstance), _
e.WorkflowInstance)
End Sub
Private Sub UnloadInstance( _
ByVal workflowInstance As Object)
CType(workflowInstance, WorkflowInstance).TryUnload()
End Sub
Private Sub OnWorkflowLoaded( _
ByVal sender As Object, _
ByVal e As WorkflowEventArgs)
Console.WriteLine("Workflow loaded.")
End Sub
In the Click event of the Start button, create a new thread that invokes the
StartWorkflow() subroutine, which will be defined next:
Private Sub btnStart_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnStart.Click
Dim t1 As New Thread(AddressOf StartWorkflow)
t1.Start()
End Sub
The
StartWorkflow() subroutine creates a new workflow instance and starts it. You must start it on a separate thread because once the workflow instance is started, the user interface of the Windows application will be frozen and it will then be impossible to click on the Unload button to unload the workflow.
Private Sub StartWorkflow()
workflowInstance = _
workflowRuntime.CreateWorkflow( _
GetType(WorkflowLibrary1.Workflow1))
workflowInstance.Start()
'---wait for the event to be signaled---
WaitHandle.WaitOne()
End Sub
To manually unload an executing workflow instance, use the
Unload() method, as shown here in the Click event of the Unload button:
Private Sub btnUnload_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnUnload.Click
Try
workflowInstance.Unload()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
Once a workflow instance is unloaded, it will be persisted to the SQL database. To load the workflow instance, code the Click event of the Load button and use the
Load() method:
Private Sub btnLoad_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnLoad.Click
Try
workflowInstance.Load()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
You are now ready to test the application. Set the Windows application as the startup project and press F5. To view the messages printed by the
Console.WriteLine() statements, select View>Output in Visual Studio 2005.
Figure 10 shows what happened when I allowed the workflow to run from beginning to end. In this example, I clicked on the Start button and let the workflow execute till its conclusion.

Figure 10. Here I've run a workflow from beginning to end with no interruptions.
|
|

Figure 11. Here I interrupted the workflow by manually requesting to unload and load a workflow instance. |
Figure 11 shows what happened when the workflow is started and then unloaded and finally loaded again. I clicked the Start button to start the workflow and, later, unload (by clicking on the Unload button) while it was still executing. Finally, I clicked the Load button to load the workflow instance from the SQL database.
In this article, you have seen how long-running workflows can be persisted to external media such as a SQL Server database. You have also seen how to manually unload a workflow instance from memory. Persisting workflows is important as it conserves valuable memory and allows you to execute long-running workflows without worrying about resource constraints.