WCF/WF - How to persist a suspended workflow - Asked By Martina Loescher on 26-Oct-11 01:33 PM

Hi together,
if a workflow suspends with suspend activity or by calling the suspend-method of the runtime the workflow is usually not persisted in persistence store. But we need to do this to make it possible to resume the workflow from another application or after the hosting application had stopped caused by an error. How can i persist the suspended workflow. The SaveWorkflowInstanceState-Method of Persistence Service is not called by the runtime automatically!
Jitendra Faye replied to Martina Loescher on 27-Oct-11 12:17 AM

Solution-

Create the persistence database.  because it persists the workflows that are suspended for longer than a given period of time (taking them out of memory).

Here is a link to help you create the database and use it in your workflow: http://msdn.microsoft.com/en-us/library/ms735722(VS.85).aspx

In the link, it mentions changing your app.config. Like this:


//Add the persistence service WorkflowPersistenceService persistenceService = new SqlWorkflowPersistenceService( DBConnections.PersistenceService, true, TimeSpan.MaxValue, new TimeSpan(0, 0, 15)); m_WorkflowRuntime.AddService(persistenceService);


Hope this will help you.
smr replied to Martina Loescher on 27-Oct-11 05:32 AM
hi

First step is setting up the new persistence databases. Go to “C:\Windows\Microsoft.NET\Framework\v4.0.30319\SQL\en” and there you will find two files:
- SqlWorkflowInstanceStoreSchema.sql
- SqlWorkflowInstanceStoreLogic.sql

In SQL Server (Express or above), create a database with a convenient name (my database is called “SqlWorkflowInstanceStore”) and run the above scripts to create the required objects.

The persistence model in WF 4 is different than that of WF 3.5. the new model is now called InstanceStore instead of PersistenceProvider. For WF 3.5, the PersistenceProvider databases are still available in the same location:
- SqlPersistenceProviderLogic.sql
- SqlPersistenceProviderSchema.sql

    For tracking in 4.0 there is no tracking participant that writes to a SQL. The Tracking_Schema.sql applies to 3.5 WFs. Out of the box with WF 4.0 there is a tracking participant that writes to ETW (Event Tracing for Windows). Again, those files for WF 3.5 are:
- Tracking_Schema.sql
- Tracking_Logic.sql

Building the WF

Create a new Console Workflow Application, and use the default Workflow1.xaml to build the following simple workflow:


The Receive and SendReplyToReceive shape receives an empty request and returns the workflow instance id to the caller. The CodeAcitivty1 is a custom activity that retrieves the workflow instance id. Below is the code for this activity:


public sealed class CodeActivity1 : CodeActivity
{

protected override Guid Execute(CodeActivityContext context)
{
return context.WorkflowInstanceId;
}
}


The important catch is to set the property PersistBeforeSend on the SendReplyToReceive shape. This will cause the WF to persist just before sending the response to the caller. This means that now the WF will have a point to go back to in case the WF is suspended or unloaded…more on that later.

The Delay activity is used to delay the WF for 1 minute…we will see why is that required after moments.

Now go to the Program.cs file and let’s write the code required to host the WF and set up persistence.

Use the following code and then I will explain it:


class Program
{
static void Main(string[] args)
{
//WorkflowInvoker.Invoke(new Workflow1());

string baseAddress = "http://localhost:8089/TestWF";

using (WorkflowServiceHost host =
new WorkflowServiceHost(new Workflow1(), new Uri(baseAddress)))
{
host.Description.Behaviors.Add(new
ServiceMetadataBehavior() { HttpGetEnabled = true });


host.AddServiceEndpoint("IService",new BasicHttpBinding(), baseAddress);


SqlWorkflowInstanceStore instanceStore = new SqlWorkflowInstanceStore(@"Data Source=.\SQLServer2008;Initial Catalog=SqlWorkflowInstanceStore;Integrated Security=True");
host.DurableInstancingOptions.InstanceStore = instanceStore;


host.Open();
Console.WriteLine("Car rental service listening at: " +
baseAddress);
Console.WriteLine("Press ENTER to exit");
Console.ReadLine();
}

}
}



By default, the WorkflowInvoker is used to invoke the WF. I changed this into the WorkflowServiceHost, the reason is that the WorkflowInvoker cannot be configured with Persistence. WorkflowServiceHost is used to host WF Services. The other method of calling (hosting) workflows is the WorkflowApplication which – like WorkflowServiceHost – can be used to host long running asynch workflows with extensions (Persistence, Tracking) however only for workflows that are non services.

After creating my WCF endpoint and adding it to the host, I use the SqlWorkflowInstaceStore class to configure my persistence store, which is the same store I prepared when I first started.

Creating the Client

Run the WF so that the host is running and the WCF endpoint is listening for requests on “http://localhost:8089/TestWF”
Now create a client (console) application and add a service reference to the above URL. Write the following code to invoke the service operation:


ServiceReference1.ServiceClient proxy = new ServiceReference1.ServiceClient();

Guid workflowId = (Guid)proxy.Operation1();


Console.WriteLine("client done");
Console.WriteLine("enter key");
Console.ReadLine();


Run the client and observe the scenario: once the operation is invoked (and the workflow instance id is returned), open the table “InstancesTable” and you will see a record for a persisted WF. Why is that? Recall that we set a property on the SendReply shape to persist just before sending the reply. For this the WF has persisted state in the database but is still running as we did not suspend or unload the WF. See images below:




For how much time will this record appear in the database? For 1 minute because we have a delay shape in the WF to delay completion for 1 minute after which the WF execution will complete and the record will disappear from the database.

folow  http://thedotnethub.blogspot.com/2010/07/wf-40-workflowservicehost-persistence.html
smr replied to Martina Loescher on 27-Oct-11 05:41 AM
hi

 

This sample WF workflow helps to illustrate the functionality of the persistence service. The workflow consists of a sequence of three activities defined in Table 2.

Activity

Activity Type

Function

Before Serialize

Code

Prints the phrase “Before Serialize” to the console

Delay1

Delay

Suspends the workflow execution for a period of 50 seconds

After Serialize

Code

Prints the phrase “After Serialize” to the console

This figure illustrates a graphical representation of the workflow.

The following code shows a sample persistence service that serializes the workflow instance state to a file.
public class FilePersistenceProvider: StatePersistenceService
        {
                public FilePersistenceProvider(string basedir)
                {
                        FBaseDir = basedir;
                }
  
                private string FBaseDir;
                  
                public override void SaveWorkflowInstanceState(Activity rootActivity, bool unlock)
                {
                        ActivityExecutionContextInfo contextInfo = (ActivityExecutionContextInfo)rootActivity.GetValue(Activity.ActivityExecutionContextInfoProperty);
                        SerializeActivity(rootActivity, contextInfo.ContextGuid);
                }
  
                // load workflow instance state
                public override Activity LoadWorkflowInstanceState(Guid instanceId)
                {
                        object obj = DeserializeActivity(null, instanceId);
                        return (Activity)obj ;
                }
                  
                // unlock workflow instance state.
                // instance state locking is necessary when multiple runtimes share instance persistence store
                public override void UnlockWorkflowInstanceState(Activity state)
                {
                        //not implemented...
                }
  
                // save completed scope activity state
                public override void SaveCompletedContextActivity(Activity rootActivity)
                {
                        ActivityExecutionContextInfo contextInfo = (ActivityExecutionContextInfo)rootActivity.GetValue(Activity.ActivityExecutionContextInfoProperty);
                        SerializeActivity(rootActivity, contextInfo.ContextGuid);
                }
  
                // Load completed scope activity state.
                public override Activity LoadCompletedContextActivity(Guid activityId, Activity outerActivity)
                {
                        object obj = DeserializeActivity(outerActivity, activityId);
                        return (Activity)obj ;
                }
  
                private void SerializeActivity(Activity RootActivity, Guid id)
                {
                        string filename = FBaseDir + "\\" + id.ToString() + ".bin";
                        FileStream stream = new FileStream(filename, FileMode.OpenOrCreate);
                        RootActivity.Save(stream);
                        stream.Close();
                }
  
                private object DeserializeActivity(Activity RootActivity, Guid id)
                {
                        string filename = FBaseDir + "\\" + id.ToString() + ".bin";
                        FileStream stream = new FileStream(filename, FileMode.Open);
                        object Result = Activity.Load(stream, RootActivity);
                        return Result;
                }
        }
The steps to add this service to the workflow runtime are similar to what we did with the SqlStatePersistenceService. The following code shows how to add the FilePersistenceProvider to the workflow runtime.
WorkflowRuntime workflowRuntime= new WorkflowRuntime();
FilePersistenceProvider customservice = new FilePersistenceProvider("c:\\WF");
workflowRuntime.AddService(customservice);
follow http://weblogs.asp.net/gsusx/archive/2005/10/05/426699.aspx

Anoop S replied to Martina Loescher on 27-Oct-11 10:20 AM

Simulating a Suspended Workflow

To see AppFabric in action, we must simulate a broken workflow. When that occurs, the persisted workflow is suspended. To simulate this we will “break” the HRApplicationServices application temporarily by renaming the \mailbox folder to \mailbox2.

To interrrupt the HRApplicationServices workflow

  1. Navigate to the root of the computer’s hard disk.

  2. Rename the mail folder from \mailbox to \mailbox2

  3. In Internet Explorer, type or paste the application URL:

    http://localhost/HRApplicationServices
    
  4. In the application form, set the education level to Masters.

  5. Click the Apply button.

  6. In Windows Explorer open the mailbox2 folder. Note that no message file has been generated.

Viewing and Resuming a Suspended Workflow

Now that the workflow has been disrupted, AppFabric has persisted the suspended workflow. After fixing the application, you can resume the workflow.

To view the suspended workflow

  1. Open or return to IIS Manager.

  2. Under Connections, navigate to the HRApplicationServices node. Click the node to select it.

  3. Double-click AppFabric Dashboard.

  4. In the Persisted WF Instances section there is one suspended instance. Also note that there are no service exceptions or user defined errors listed. This is because the sample application did not throw an error that was caught by the client. But because the workflow was disrupted, AppFabric persisted the suspended workflow.

  5. Under the Persisted WF Instances section, double-click the suspended instance SubmitApplication.xamlx. The persisted instance appears in the list with the status of Suspended

    Suspended Instance
  6. Right-click the suspended instance and click View Tracked Events. (Other choices include Resume, Cancel, Terminate and Delete.)

  7. In the list of tracked events, click the event that is has an Error icon. The Details section below the list now contains the details of the selected error event.

    Error in Suspended Event
  8. Click the Errors tab.

    Error Detail
  9. Note the exception text. It includes the following:

    System.Net.Mail.SmtpException: Failure sending mail. ---> System.IO.DirectoryNotFoundException: Could not find a part of the path 'c:\mailbox

    In this scenario, that information is enough to fix the application. AppFabric has captured the exception text for analysis.

  10. Open Windows Explorer and rename the folder from \mailbox2 to \mailbox.

  11. Return to the AppFabric dashboard.

  12. Right-click the dashboard and click Appfabric Dashboard.

  13. Under the Persisted WF Instances section double-click the suspended instance SubmitApplication.xamlx.

  14. Right-click the suspended instance and click Resume.

  15. After a few seconds, right-click the instance again and click Refresh.

  16. In Windows Explorer open the mailbox folder. Note that there is now a message file in the folder.

  17. Double-click the item to open it and click Review the application. (If you do not have a mail-reader program, view the message with Notepad.exe and copy and paste the URL in the message to the application into your browser to see the HTML message. The URL resembles the following: http://localhost/HRApplicationServices/HireApproval.aspx?AppID=23 Note that the AppID is set to the applicant ID.)

  18. On the HTML page, click Hire to hire the applicant. Open the mailbox folder and note there is a final message file. Open it to see the good news.

  19. Return to the AppFabric dashboard.

  20. Right-click the dashboard and click Refresh. The dashboard will be updated and the idle instance will disappear from the Active or Idle Instances column.


    http://msdn.microsoft.com/en-us/library/ee677326.aspx

Martina Loescher replied to Anoop S on 27-Oct-11 11:25 AM
Thanks a lot for your tips.

As i read in many documents, in wf 3.0 (which we use) a suspended workflow is not persisted by runtime per default even when there is a persistence service running. It stays in memory until it's resumed. To persist the suspended workflow we must subscribe to the WorkflowInstanceEvent WorkflowSuspended and unload the workflow explicitely using the eventargs.workflowInstance.Unload() - method.

Greetings Martina