【问题标题】:Is it possible to persist a state machine built in Workflow Foundation to a DB using Nhibernate?是否可以使用 Nhibernate 将 Workflow Foundation 中内置的状态机持久化到数据库中?
【发布时间】:2010-11-10 13:00:03
【问题描述】:

我有一个运行时包含多个状态机实例的应用程序。我希望将这些隐式持久化到数据库中,以便如果应用程序重新启动,它们可以从数据库中重新加载,并且它们将恢复到完成时的相同状态,以及已设置的任何对象属性当实例在内存中时。

这样的事情可能吗?

我一直在徒劳地搜索 Google,这意味着以下三件事之一:

  1. 这是不可能的!
  2. 我不擅长使用 Google
  3. 对这个问题的一个体面的回答会帮助很多其他人

我希望答案是 3! :)

【问题讨论】:

  • 为什么不只使用内置的 SQL 持久性?它不需要您进行太多额外的编码,并且可以完成您描述的所有事情。如果您尝试手动执行此操作,那将是很多工作,假设您甚至可以正确序列化工作流本身。
  • 我的主要原因是因为我的后端数据库将是 Oracle - 我的理解是 SqlWorkflowPersistenceService 仅支持 SQL Server 系列数据库。这是我应该在原始问题中解释的内容。

标签: c# nhibernate xml-serialization workflow-foundation


【解决方案1】:

是的,但这不是野餐。

我有这个实现正在运行,它需要做很多工作才能完成。我不会提供整个实现(大量代码且未公开),但我会给出让我得到最终结果的构建块。

  • 位于WF_WCF_Samples\WF\Application\PurchaseProcess\CodedWorkflow\CS\WfDefinition\XmlWorkflowInstanceStore.cshttp://msdn.microsoft.com/en-us/library/dd483375.aspx 有一个用于工作流的 XML 持久化程序。这规避了需要拥有 SQL 表的问题,只允许您将整个内容存储到一个 XML blob 中;

  • 此序列化程序(反)序列化带有NetDataContractSerializer 的代码。此序列化程序需要有一个 SurrogateSelector 集,您可以使用它来翻译您的实体。问题是您不想存储实体。除了所有惰性实体将与您的实体一起序列化之外,当您的 XML 被反序列化时,您的实体将过时。相反,您想要的是只有内部 ID 被序列化。所以,你要做的是实现一个SurrogateSelector 来检查被序列化的对象是否是一个实体。如果是这样,则将其替换为不同的类,例如 EntityReference,它具有类型和 ID;

  • 当反序列化 XML 时,使用相同的 SurrogateSelector,您检查 EntityReference 的实例。如果你发现了这个,你可以使用引用中的类型和 ID 执行 ISession.Load(),然后返回它而不是引用。

这些是序列化和反序列化工作流的基本构建块。这不是一个简单的壮举,但这些部分我得到了它的工作。

希望对你有所帮助。

【讨论】:

  • 考虑解耦:WF 作为路径、状态、事件、流程,但没有业务对象。为 WF 和进程状态创建 WF 特定对象的小子域。应用程序状态在标准域中。 WF 现在是流程/操作源,而不是数据接收器。 WF 通过对域对象的服务调用作用于数据。序列化复杂性/版本/未使用的开销消失了。良好的关注点分离,域与WF的解耦让WF更容易变化和改变,域模型仍然是应用的本质。工作流是关于过程的,OOP 不擅长过程。工具的良好协同作用...
  • ... 这个设计的关键是WF对象子域包含流程规则,而业务规则留在域中。积极重构以实现干净的分离,业务很少知道自己的流程。项目失败。应用程序是遗留的端口。新的网络应用程序,相同的旧流程。软件的真正承诺从未实现,因为企业无法重新设计其流程以充分利用软件。从业务中定义和提取流程,软件成为工具,通过 WF/流程研究,帮助重新调整流程与软件本身。这是一个非常强大的概念。
  • 感谢大家的cmets。
【解决方案2】:

Nhibernate 对此并不是特别有用,因为物理持久性操作是工作流持久性的一个次要方面。所涉及的部分是支持在适当的时间卸载重新加载和激活工作流等的所有服务。运行时和服务的内部工作也由于工作流跨越可以无限持续的应用程序边界这一事实而变得复杂。各种实体可以在任何给定时间处于各种状态。由于在这些条件下甚至不能保证调用具有客户端,因此当您在工作流中引发事件时,运行时实际执行的操作是将消息放入队列中,而不是调用实际的函数指针。这就是您能够在未运行的工作流中“引发”事件的方式。

您可以访问这些消息队列和运行时的其他低级详细信息,但最好尽量留在 Microsoft 框架内并尽可能避免重新构建任何内容。 Si 当你按照他们的方式做事时,这比尝试自己管理要容易得多,Nhibernate 变得相当没有意义,因为它所做的只是保持二进制序列化流。

在可用的各种扩展中,其中之一是能够创建可在现有工作流框架内运行的自定义持久性提供程序。已经编写了一个 Oracle 提供程序。无法证明它的可靠性,但您至少应该能够以此为基础。

http://wftools.codeplex.com/

【讨论】:

    【解决方案3】:

    这会是一个合适的解决方案吗?

    使用我在以下文章的下载链接中发现的 FilePersistence.cs 自定义持久性处理程序:

    http://msdn.microsoft.com/en-us/library/ms741725%28v=VS.85%29.aspx

       public class FilePersistenceService : WorkflowPersistenceService
    {
    
        public FilePersistenceService(bool unloadOnIdle)
        {
           // ...
        }
    
        protected override void SaveWorkflowInstanceState(Activity rootActivity, bool unlock)
        {
            // ...
        }
    
        private void ReloadWorkflow(object id)
        {
            // ...
        }
    
        protected override Activity LoadWorkflowInstanceState(Guid instanceId)
        {
            // ...
        }
    
        protected override void UnlockWorkflowInstanceState(Activity state)
        {
            // ...
        }
    
        protected override void SaveCompletedContextActivity(Activity activity)
        {
            // ...
        }
    
        protected override Activity LoadCompletedContextActivity(Guid activityId, Activity outerActivity)
        {
            // ...
    
        }
    
        protected override bool UnloadOnIdle(Activity activity)
        {
            // ...
        }
    
        private void SerializeToFile(byte[] workflowBytes, Guid id)
        {
            // ... Direct workflowBytes to DB, indexed by Guid
        }
    
    
        private byte[] DeserializeFromFile(Guid id)
        {
            // ... Load bytes from DB, by Guid
        }
    
    }
    

    ...覆盖 SerializeToFile 和 DeserializeFromFile 以将调用重定向到数据库而不是文件系统。

    我可能会使用 NHibernate 来完成加载/保存 - 以保持我的 DAL 在整个应用程序中通用,并希望保持 DB 独立性(如果需要使用另一个 DB 平台)。

    这个计划有什么重大缺陷吗?

    【讨论】:

    • 是的,当我说您可能会使用 NHibernate 来做的只是保持二进制流时,这种设置基本上就是我的意思。但是,请注意示例代码是如何重新启动的——它们只是设置了一个系统计时器来调用 load。我想使用 Oracle 的客户可能只创建了少数工作流,但是......您可能需要创建一个轮询服务来模拟 SQL Persistence 通过检查过期计时器等的工作方式,以便您只检索和反序列化工作流什么时候真正运行,而不是看它是否准备好了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-17
    • 1970-01-01
    相关资源
    最近更新 更多