I'm using Windows WF right now for a simple way to state machines. In fact, I'm not even using a state machine, I'm using a sequential workflow. Eventually, I will ditch WF in favor of something else, but since I already have the code working, I need to get the Abort, Suspend, and Resume methods working.
My application spawns a thread, which then spawns another thread that owns the WorkflowInstance. My GUI has Abort, Pause, and Resume buttons in it, and these eventually call the WorkflowInstance's Abort, Suspend, and Resume methods, respectively.
The problem is that when I do this, I get a very large and scary MessageBox that says:
The workflow hosting environment does not have a persistence service as required by an operation on the workflow instance
along with a nice stack trace and all. Now, I looked up these methods in Pro WF by Bruce Bukovics and one of his examples calls these methods, and no mention of a "persistence service" was anywhere. However, his example calls were within the scope of the WorkflowRuntime, i.e he calls them like this:
using(WorkflowRuntimeManager manager = new WorkflowRuntimeManager(new WorkflowRuntime("WorkflowRuntime")))
{
manager.WorkflowRuntime.StartRuntime();
WorkflowInstanceWrapper instance = manager.StartWorkflow(typeof(SharedWorkflows.Workflow1), null);
instance.Suspend("Manually suspended");
instance.Resume();
waitHandle.WaitOne();
}
In my app, I implemented the WorkflowRuntime as a singleton because I found that there was a huge memory leak when I created the WorkflowRuntime like this. So my code looks like this:
WorkflowInstance instance = WorkflowRuntimeSingleton.Instance.workflow_runtime.CreateWorkflow(typeof(SharedWorkflows.Workflow1), null);
instance.Start();
instance.Suspend("Manually suspended");
instance.Resume();
waitHandle.WaitOne();
Now, if I call Suspend and Resume as shown above, it works fine. But if I issue the call via my GUI, it complains about the persistence service.
Given this information, and that I do not want to set up a database just to get these three functions, I'd like to know what I need to do to make this work. My best guess at this point is that WF doesn't like being controlled from a separate thread. If that's the case, is there a good way to make the call seem as though it is issued from the same thread?
Here are some possible solutions that I've come up with, but I'm sure someone here has a way fancier and elegant way to do it.
- WF polls for abort / pause / resume via an interface to GUI (seems really lame)
- Replace the WaitOne() with WaitAny(), and have the GUI call into the object that owns the workflow set an AutoResetEvent. WaitAny() allows execution to continue, and then my code can check to see which button the user pressed. This would need to be wrapped in a loop so that we can wait again until the user clicks Abort, or until the WF is complete.
- use a boolean flag to basically do what #2 is doing.
- see if anyone on SO knows how to make the call magically come in on the right thread :)
Any insight or opinions would be really appreciated!