RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Execute a Long-Running Workflow Using Persistence in Windows WF : Page 3

If you're experimenting with workflows it won't be long before you find that you need to run a workflow that is too long to execute directly from memory. A workflow that needs hours—or even days—to conclude requires you to persist the workflow instance to an external medium so that the workflow can be continued later.

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)
    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.")
    End Sub

    Private Sub OnWorkflowTerminated( _
     ByVal sender As Object, _
     ByVal e As WorkflowTerminatedEventArgs)
        Console.WriteLine("Workflow terminated.")
    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), _
    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)
    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( _
        '---wait for the event to be signaled---
    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
        Catch ex As Exception
        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
        Catch ex As Exception
        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.

Wei-Meng Lee is a Microsoft MVP and founder of Developer Learning Solutions, a technology company specializing in hands-on training on the latest Microsoft technologies. He is an established developer and trainer specializing in .NET and wireless technologies. Wei-Meng speaks regularly at international conferences and has authored and coauthored numerous books on .NET, XML, and wireless technologies. He writes extensively on topics ranging from .NET to Mac OS X. He is also the author of the .NET Compact Framework Pocket Guide, ASP.NET 2.0: A Developer's Notebook (both from O'Reilly Media, Inc.), and Programming Sudoku (Apress). Here is Wei-Meng's blog.
Email AuthorEmail Author
Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date