工作流版本管理一直是大家非常感兴趣的话题。在刚刚过去的Tech.Ed 2009和MVP Open Day 2009中,通过和一些开发人员的交流,我发现许多人心中都有这样一些问题:WF 4.0支不支持工作流版本管理?如果支持,要怎么做?今天,我们就来谈谈这一话题。

什么是工作流版本管理

工作流通常用来对企业、政府或其他组织内部或之间的业务逻辑进行建模,一个业务逻辑通常对应一个工作流定义,每个工作流定义通常会有多个工作流实例运行。举例来说,企业内部都有报销流程,在工作流系统中就会有一个工作流定义用来实现这个报销流程,而企业中会有很多人需要报销,因此这个报销工作流会有很多实例运行。

业务逻辑通常不是一成不变的。当某一业务逻辑在已经有对应的工作流实例运行的情况下需要做出改变,就会涉及到一些问题。比如现在有10个报销工作流的实例在运行,而企业希望调整报销流程,应该怎么办呢?这一问题就是工作流版本,也就是说某一工作流定义需要根据业务流程的变化做出改变,因此产生了针对某一业务逻辑的多个版本的工作流定义。这一问题的解决方案就是工作流版本管理。

理想情况下,工作流版本只是工作流定义在服务器端做出的改变,对客户端来说是透明的。换句话说,已经部署并运行的客户端不需要做任何改动就可以继续与工作流交互(当然,这一切的前提是工作流定义的改变并不会影响客户端与其的交互方式)。然而,我们仍然要思考以下几个问题:

  1. 不同版本工作流定义的实例可以同时运行吗?
  2. 已经在运行的工作流实例应该升级到最新版本吗?
  3. 如何把客户端的请求路由到不同版本的工作流实例呢?

针对这些问题,通常可以用以下几种典型策略概括。

  1. 新工作流实例创建机制。当需要创建一个新工作流实例时,服务器必须要选择使用哪个工作流版本。
    1. 最新创建:服务器永远会使用最新的工作流版本来创建工作流实例。
    2. 选择创建:服务器通过某种判断逻辑来决定使用那个工作流版本。判断逻辑可以完全取决于服务器端,也可以是客户端显示指定需要那个版本。这种策略非常灵活,当然也需要服务器端做更多的事情。它的另一个好处就是可以控制流程的升级。比如可以先选择性的在某些实例上进行升级,验证新版本是否合适,然后再做全面升级。
    3. 延迟创建:服务器可以选择在已有工作流实例结束之前拒绝所有创建新流程的请求。
  2. 已有工作流实例处理机制。当工作流定义出现多个版本时,服务器必须决定如何处理那些已经存在的基于老版本的工作流实例。
    1. 终止实例:马上终止老版本的工作流实例。这样一来,就需要创建新实例来代替被终止的老实例。如果业务逻辑要求老版本和新版本不能共存,那么这种策略是比较合适的选择。
    2. 逐步取消:已有的工作流实例不会受到任何影响,它们会继续执行。当所有这些已有老实例全部结束后,老版本的工作流定义将会被弃用。
    3. 完全升级:所有老实例全部都升级到新版本。
    4. 选择性升级:某一部分老实例会被升级。更复杂一点的情况是当有多余两个版本存在时,比如有A、B、C三个版本,A是最初的版本,某些老实例可能不升级,仍然维持在版本A,某些老实例可能会被升级到版本B,某些老实例可能会被升级到版本C。

WF 4.0中的工作流版本管理

WF 4.0并不提供任何高层次的工作流版本管理功能。这意味着默认情况下,你无法查看或管理工作流定义的不同版本,你无法在你的应用中直接拥有以上介绍过的各种机制和策略。但是作为一个基础框架,WF 4.0允许你实现自己的工作流版本管理功能。下面我们就来看看如何实现。

从技术上来说,在新工作流实例创建机制方面,WF 4.0支持上面提到的全部三种策略;在已有工作流实例处理机制方面,WF 4.0不支持已有老版本工作流实例的升级(也就是说它不支持2.3和2.4)。已有工作流实例必须要么执行结束(2.2),要么被终止或被新版本的工作流实例替换(2.1)。注意我说“从技术上来说”是因为WF 4.0并不默认提供这些机制和策略,你需要自己实现。

其实,无论是创建新工作流实例还是处理已有工作流实例,无论选择哪种策略,需要解决的问题 只有两个:

  1. 如何承载不同版本的工作流定义(Hosting)?
  2. 如何路由客户端请求到不同的版本(Routing)?

在开始进入这两个问题之前,需要先介绍一下WF 4.0的承载机制。在WF 4.0中,工作流定义可以笼统的分为两类:普通工作流和工作流服务(Workflow Service)。普通工作流顾名思义就是最普通的工作流定义,它有一些活动组成。工作流服务比较特别,它是由工作流实现的网络服务。一个工作流服务可以包含多个服务方法(Service Operation),客户端可以使用标准的网络服务协议调用这些服务方法,从而触发工作流实例的初始化和流转。在WF 4.0中,工作流服务的底层是由WCF实现的。

针对这两种工作流定义,WF 4.0提供了两种不同的承载方式:WorkflowApplication和WorkflowServiceHost。WorkflowApplication针对普通工作流,WorkflowServiceHost针对工作流服务。你可以在自己的程序中通过代码来创建这些宿主。对于工作流服务,你还可以把它们部署到IIS中,后者会自动创建WorkflowServiceHost。

承载(Hosting)

在WF 4.0中,每个工作流定义都必须运行在一个单独的工作流宿主中。也就是说,如果一个工作流定义有多个版本需要同时被使用,开发人员必须创建相应数量的工作流宿主。这些宿主可以运行在同一个进程中,也可以运行在不同进程中。

这听起来似乎不是很方便,但实际上并非如此。因为大部分的工作流定义(尤其是涉及到人工活动的工作流),我们都建议实现成工作流服。在部署工作流服务时,只要把它们发布到IIS上,IIS就会自动创建相应的WorkflowServiceHost,你不需要做任何额外的工作。如果你需要使用WorkflowApplication,在代码中创建它们也不是什么难事儿。

路由(Routing)

相比承载,路由是一个略显复杂的问题。根据你定义的是一个普通工作流还是工作流服务,可以有不同的实现方式。

自定义的版本路由控制器

自己实现工作流版本的路由控制器。这样做的好处是有最大的灵活性,既可以用于普通工作流,也可以用于工作流服务;坏处是你自己需要实现所有的逻辑(显而易见J)。

对于普通工作流的情况,它可以是一段决定应该使用哪个工作流版本的代码。一个客户端请求会被先发送到这个路由控制器,然后控制器会决定使用哪个工作流版本并调用相应代码创建该版本的工作流实例。

对于工作流服务的情况,它可以是一个网络服务。客户端在申请创建一个工作流实例前先发送一个请求到该控制器,然后控制器会就决定使用哪个工作流版本并返回相应的工作流服务地址。随后客户端再发送请求到这个地址以便创建工作流实例。

WCF路由服务

如果使用工作流服务,那么一种更好的方法是使用WCF路由服务。WCF路由服务是WCF 4.0中提供的新功能,它可以帮助你非常方便的实现网络服务路由。因为工作流服务的底层实现就是WCF,因此我们可以利用WCF路由服务来实现版本路由。在这种方法中,每一个客户端请求都需要在其请求参数中包含一个版本令牌(version token)。WCF路由服务会根据这个版本令牌把请求转发到相应的工作流服务。当然我们也可以实现一些默认逻辑,例如对不包含版本令牌的请求,一律路由到工作流的最新版本。

关于WCF路由服务以及WCF 4.0新功能,请参考这篇文章A Developers’ Introduction to Windows Communication Foundation 4

后续

以上就是在WF 4.0中实现工作流版本管理的方式。可以看到,虽然WF 4.0默认并没有提供高层次的版本管理功能,但是实现一套自己的版本管理并不困难。今天我介绍了不少理论上的东西,在下一篇文章中,我将进入实战环节,实现一个简单的工作流版本管理,与大家共同讨论。