Managing workflow lifecycle without a "persistence service"
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(SharedWorkflow开发者_运维知识库s.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!
Its not that big of a deal to create the persistence database. In fact, it will help your memory problems, 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. I didn't do this. Instead, I added the service in code. Like this:
//Add the persistence service
WorkflowPersistenceService persistenceService = new SqlWorkflowPersistenceService(
DBConnections.PersistenceService,
true,
TimeSpan.MaxValue,
new TimeSpan(0, 0, 15));
m_WorkflowRuntime.AddService(persistenceService);
EDIT: Another helpful link
SQL Server Compact won't work because CE does not support stored procedures, which are part of the default persistence DB creation script and are invoked by the SQL Persistence Service. CE is meant to embed the engine within an application. DB file would be used with something like SQL Server Express, where the engine is run in a separate process but you can point to a DBF file rather than connecting to a DB already attached to the engine.
By your question/response, it sounds like you don't expect a workflow created with one instance of the app to be recalled with another instance (shared workflows). One additional possibility is not to use the SQL version of the persistence service. There is a sample (may not be complete, not sure) of a workflow persistence service that is based upon direct serialization of the workflow to a file at http://msdn.microsoft.com/en-us/library/ms741725.aspx. I don't know if it supports all your needs, but since it includes source, you may be able to tweak it.
I'm posting this as an answer so that I can get decent formatting. I have followed Gabriel's answer, and I am having the darnedest time trying to get the database configured. I have several questions regarding this.
All of the links mention creating the database using Microsoft SQL Server Query Analyzer, which I don't have. Instead, I went to the Server Explorer in VS2008, right-clicked Data Connections -> Create New SQL Server Database. I used Windows Authentication and selected my computer from the droplist of servers.
I want to be able to run this code on multiple systems, using the same database file. Why can't I specify localhost here?
Regarding the above question, if I instead create a new database using Data Connections -> Add Connection, I can create a local database file that I can include in my solution, and presumably move from PC to PC. This is probably the right way to go, but
What's the major difference between using "Microsoft SQL Server Compact 3.5" and "Microsoft SQL Server Database File"? Both allow me to create a file. I like the Compact selection better, because I don't need to use a password, but I don't know if this then requires some other special service to be installed on another computer that the Database File option doesn't need.
Moving on, I then have to execute an SQL query to generate the table for the workflow persistence store. According to the linked pages, this location is:
%WINDIR%\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\<language>\SqlPersistence_Schema
Since I don't have the Query Analyzer, I figured that executing a query from VS2008 should work well enough. If I copy and paste the SQL into the query window, I get this error dialog:
Query Definitions Differ
The following errors were encountered while parsing the contents of the SQL pane:
The Set SQL construct or statement is not supported.
followed by:
SQL Execution Error.
Executed SQL statement: -- Copyright (c) Microsoft Corporation. All rights reserved.
SET NOCOUNT ON
--
-- ROLE state_persistence_users
--
declare @localized_string_AddRole_Failed nvarchar(256)
set @localized_string_AddRole_Failed = N'Failed adding the "state_per...
Error Source: .Net SqlClient Data Provider
Error Message: Incorrect syntax near the keyword 'if'.
Incorrect syntax near 'GO'.
Incorrect syntax near the keyword 'CREATE'.
Incorrect syntax near the keyword 'IF'.
Incorrect syntax near 'GO'.
Incorrect syntax near the keyword 'CREATE'.
Incorrect syntax near the keyword 'CREATE'.
Incorrect syntax near the keyword 'DBCC'.
Incorrect syntax near ')'.
Does anyone know other ways I can try to create the persistence store?
精彩评论