Workflow 4.0 offers a powerful persistence features, which are at this moment not in detail documented. For this reason I build a short sample, which demonstrate persistence of the workflow driven by workflow host. The workflow example shown in this post is implemented as fully declarative (all in XAML) workflow. Persistence of the workflow is explicitly requested by using of Persist activity.
Next picture shows the workflow used in this test and full XAML can be downloaded here.
The workflow receives one integer as argument. The entered integer value is decremented in the while-loop, which contains a Delay activity and Persist-activity.
Without of Persist activity, workflow would be started, the entered value would be decremented in the loop and so on to the end of workflow.
Assuming that the loop in the workflow instead of delay activity would implement some long running business, the probability the things could become more complicated. Because the business id by definition important, the probability of an error increase with the increasing of time of running of the workflow. The longer running time of the workflow the higher error probability.
Now the question is, “what will happen if the machine running the workflow is switched off somewhere in the middle of execution”? The workflow and business results in the loop would be lost. To prevent losing of data the Persistence feature can be used as shown in this example.
When Persist-activity is invoked the workflow will be safely persisted. Depending on persistence provider The safety of business data depends directly on persistence provider. At this moment WF4.0 provides out of the box SqlPersistenceProviderFactory able to create PersistanceProvider which safely persist all of data in the Sql Server database.
This is the theory, which will work. However more interesting is the code in host, which will start and load persisted workflow.
Here is the full code:
In the code shown above I do following. First I try to load the instance thinking that it has already been persisted. If it is very first start of application this call will file. This is a reason why I wrap this call in exception handler. After that I will create the new instance of workflow of use the existing one which has been loaded by WorkflowInstance.Load(..).
On very first call you will get following exception:
InstanceNotFoundException
”The requested operation could not complete because instance '45b7d274-1409-41f3-87cf-1924d465fa01' could not be found.”
I figured out, that this exception is sometimes throw even if the workflow instance is properly persisted. In this case just repeat the call and all should work fine.
Please note that if the instance is newly created (has not been persisted yet = first app start) you have to add persistence provider in the list of extensions. Do not do this on instances loaded form the persistence store, because they already have added instance provider in this list.
After all, I do usual staffs: I subscribe some handlers, run workflow and wait to the end. But take a look in OnCompletedHandler. You will notice there following:
persProv.Delete(myInstance, TimeSpan.FromSeconds(5));
This line of code ensures that the completed and persisted instance in the store is removed from the store. Depending on your business requirement you might keep it in the store or delete it. By default completed instances remains in the store. You can try for fun to load a completed instance and start it again. It will all work fine.
Now, set the breakpoint in the workflow designer at the Persist-activity and start the application. Press F5 few times and take a look on variable CuurentValue. The value will be decrease each time F5 has been pressed. Now just kill the application or stop it. Open the database specified in the connection string in very first line and look for your instance in the table InstanceData. You should notice the instance there.
Now disconnect from database (detach) and start the application again. This time the exception InstanceNotFoundException will not be thrown (otherwise repeat this step), press F5 and the program will stop again at the Persist-activity. Take a look at the value of variable CurrentValue, and you will see it is at the same value before the simulated crash of application.
I very hope this helps to make better understanding of workflow persistence.
Here is the ASCII version of the host code:
public static void StartWFWithPersistanceActivity() { Guid wfInstId = new Guid("45b7d274-1409-41f3-87cf-1924d465fa71"); m_PersFactory = new SqlPersistenceProviderFactory(@"Data Source=. \SQLEXPRESS;AttachDbFilename=C:\MyProjects \SampleInstanceStore.mdf;Integrated Security=True", false, false, TimeSpan.FromSeconds(60)); m_PersFactory.Open(); SequenceWithPersistActivity wf = new SequenceWithPersistActivity() PersistenceProvider persProv = null; persProv = m_PersFactory.CreateProvider(wfInstId); WorkflowInstance myInstance = null; try { myInstance = WorkflowInstance.Load(wf, persProv); } catch (Exception ex) { } if (myInstance == null) { Dictionary args = new Dictionary() args.Add("InputValue", 10); myInstance = new WorkflowInstance(wf, args, wfInstId); myInstance.Extensions.Add(persProv); } myInstance.OnCompleted = delegate(WorkflowCompletedEventArgs e) { Console.WriteLine("Completed"); persProv.Delete(myInstance, TimeSpan.FromSeconds(5)); syncEvent.Set(); }; myInstance.OnAborted = delegate(WorkflowAbortedEventArgs e) { Console.WriteLine(e.Reason); syncEvent.Set(); }; myInstance.OnUnhandledException = delegate (WorkflowUnhandledExceptionEventArgs e) { Console.WriteLine(e.UnhandledException.ToString()); return UnhandledExceptionAction.Terminate; }; myInstance.Run(); syncEvent.WaitOne(); }
Posted
Jun 21 2009, 12:24 AM
by
Damir Dobric