在没有“持久性服务”的情况下管理工作流生命周期

发布于 2024-08-21 09:05:05 字数 1843 浏览 2 评论 0原文

我现在使用 Windows WF 作为状态机的简单方法。事实上,我什至没有使用状态机,我使用的是顺序工作流程。最终,我将放弃 WF,转而使用其他方法,但由于我已经让代码正常工作,所以我需要让 Abort、Suspend 和 Resume 方法正常工作。

我的应用程序生成一个线程,然后该线程生成另一个拥有 WorkflowInstance 的线程。我的 GUI 中有 Abort、Pause 和 Resume 按钮,这些按钮最终分别调用 WorkflowInstance 的 Abort、Suspend 和 Resume 方法。

问题是,当我这样做时,我得到一个非常大且可怕的消息框,上面写着:

工作流托管环境没有工作流实例上的操作所需的持久性服务

以及一个很好的提示堆栈跟踪和所有。现在,我在 Pro WF by Bruce Bukovics 及其示例之一中查找了这些方法调用这些方法,并且没有提到“持久性服务”。然而,他的示例调用是在WorkflowRuntime的范围内,即他这样调用它们:

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();
}

在我的应用程序中,我将WorkflowRuntime实现为单例,因为我发现当我像这样创建WorkflowRuntime时存在巨大的内存泄漏。所以我的代码如下所示:

WorkflowInstance instance = WorkflowRuntimeSingleton.Instance.workflow_runtime.CreateWorkflow(typeof(SharedWorkflows.Workflow1), null);
instance.Start();
instance.Suspend("Manually suspended");
instance.Resume();
waitHandle.WaitOne();

现在,如果我如上所示调用 Suspend 和 Resume,它就可以正常工作。但如果我通过 GUI 发出调用,它会抱怨持久性服务。

鉴于这些信息,并且我不想仅仅为了获得这三个函数而设置数据库,我想知道我需要做什么才能使这项工作正常进行。我目前最好的猜测是 WF 不喜欢由单独的线程控制。如果是这种情况,是否有一种好方法可以使调用看起来像是从同一线程发出的?

以下是我提出的一些可能的解决方案,但我确信这里有人有一种更奇特和优雅的方法来做到这一点。

  1. WF 通过 GUI 接口轮询中止/暂停/恢复(看起来真的很蹩脚)
  2. 将 WaitOne() 替换为 WaitAny(),并让 GUI 调用拥有工作流的对象设置 AutoResetEvent。 WaitAny() 允许继续执行,然后我的代码可以检查用户按下了哪个按钮。这需要包含在一个循环中,以便我们可以再次等待,直到用户单击 Abort,或者直到 WF 完成。
  3. 使用布尔标志基本上做#2正在做的事情。
  4. 看看是否有人知道如何让呼叫神奇地进入正确的线程:)

任何见解或意见将非常感激!

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.

  1. WF polls for abort / pause / resume via an interface to GUI (seems really lame)
  2. 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.
  3. use a boolean flag to basically do what #2 is doing.
  4. 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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

任谁 2024-08-28 09:05:05

创建持久性数据库并不是什么大不了的事。事实上,它会帮助您解决内存问题,因为它会保留暂停时间超过给定时间的工作流程(将它们从内存中取出)。以下链接可帮助您创建数据库并在工作流程中使用它: http://msdn.microsoft.com/en-us/library/ms735722(VS.85).aspx

在链接中,它提到更改您的 app.config。我没有这样做。相反,我在代码中添加了该服务。像这样:

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

编辑:另一个有用的链接

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

吝吻 2024-08-28 09:05:05

SQL Server Compact 将无法工作,因为 CE 不支持存储过程,这些过程是默认持久性数据库创建脚本的一部分,并由 SQL 持久性服务调用。 CE 旨在将引擎嵌入到应用程序中。 DB 文件将与 SQL Server Express 之类的东西一起使用,其中引擎在单独的进程中运行,但您可以指向 DBF 文件,而不是连接到已附加到引擎的数据库。

根据您的问题/回答,听起来您并不期望使用应用程序的一个实例创建的工作流程会被另一个实例调用(共享工作流程)。另一种可能性是不使用持久性服务的 SQL 版本。有一个工作流持久性服务示例(可能不完整,不确定),该服务基于将工作流直接序列化为文件,位于 http://msdn.microsoft.com/en-us/library/ms741725.aspx。我不知道它是否支持您的所有需求,但由于它包含源代码,您也许可以对其进行调整。

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.

舟遥客 2024-08-28 09:05:05

我将其作为答案发布,以便我可以获得合适的格式。我已经遵循了加布里埃尔的回答,并且我正在花最长时间尝试配置数据库。对此我有几个问题。

所有链接都提到使用 Microsoft SQL Server 查询分析器创建数据库,但我没有。相反,我进入VS2008中的服务器资源管理器,右键单击数据连接->创建新的 SQL Server 数据库。我使用 Windows 身份验证并从服务器下拉列表中选择了我的计算机。

我希望能够使用相同的数据库文件在多个系统上运行此代码。为什么我不能在这里指定localhost

关于上述问题,如果我改为使用数据连接创建一个新数据库 ->添加连接,我可以创建一个本地数据库文件,可以将其包含在我的解决方案中,并且可能从一台电脑移动到另一台电脑。这可能是正确的方法,但是

使用“Microsoft SQL Server Compact 3.5”和“Microsoft SQL Server 数据库文件”之间的主要区别是什么?两者都允许我创建一个文件。我更喜欢“紧凑”选项,因为我不需要使用密码,但我不知道这是否需要在另一台计算机上安装“数据库文件”选项不需要的其他特殊服务。

接下来,我必须执行 SQL 查询来生成工作流持久性存储的表。根据链接页面,这个位置是:

%WINDIR%\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\<language>\SqlPersistence_Schema

由于我没有查询分析器,我认为从 VS2008 执行查询应该足够好。如果我将 SQL 复制并粘贴到查询窗口中,则会出现以下错误对话框:

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.

后面跟着:

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 ')'.

有人知道我可以尝试创建持久性存储的其他方法吗?

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?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文