本文转自:http://www.cnblogs.com/Mayvar/archive/2011/09/03/wanghonghua_201109030446.html

已经有不少朋友知道Workflow Foundation 4了。这个版本较之于以往有了一些明显的区别,开发的一些思路也大不相同了。

很多人会觉得很怀念以前3.0中的“顺序工作流”和“状态机工作流”的分类,其实大可不必。在4.0中虽然没有称之为“状态机工作流”的东西,但其实实现起来也很自然.WF 4提供了FlowChart的功能,其实就是状态机工作流。(它的不同Decision之间可以转化,而这正是状态机与顺序工作流的根本区别)

这一篇不是用来讨论状态机工作流这个话题的,提一下只是想说,大家要抓住重点,而不是表象。

专门写一篇来介绍一下所谓事件驱动的流程设计和应用,是因为

  • 首先,没有太多流程不需要用户交互,而如果需要用户交互,就得通过事件这样的机制。(无论是顺序型还是状态机)
  • 其次,没有太多文章和例子介绍这些细节,但WF4中的做法也不是很直观,一般人要理解起来还是蛮吃力的。

我们来假想这样一个场景,我们需要有一个文档审批的流程。很显然流程是不会自动启动的,它得由用户发出一个指令(通常是在界面上填写了一些数据,然后点击了一个按钮)。那么,这样的功能要怎么实现呢?

那么,就让我们开始吧

【备注】本文代码,可以通过 这里 下载

1.创建一个Activity Library.

请注意,我建议你创建Activity Library,不要为了省事就创建WCF Workflow Service Application或者Workflow Console Application.因为那样既不实用(你不可能在项目中这么做),也会隐藏很多细节。

将默认的那个Activity1.xaml删除掉,然后添加一个DocumentReviewWorkflow

2. 修改这个workflow的设计

在WF 4中,对Activity进行了全新的设计,3.0中的Activity几乎一个不留了。对于事件监听而言,现在是使用一个所谓的Pick的Activity

我们这个流程首先需要能够监控用户创建表单的一个行为(事件),所以,我们需要添加这样一个Pick

大致是这样的设计过程:

2.1 拖放一个Sequence到设计器中

2.2 拖放一个Pick到Sequence中,默认会有两个PickBranch,删除其中的一个

2.3 拖放一个Receive到PickBranch的Trigger里面(我们设置了这个Receive的ServiceContractName,和OperationName,你可以随便取名,没有太多限制。这里其实是使用了WCF的技术。这是WF 4的一个很大的特点:与WCF结合得很紧密)

2.4 定义一个TicketId的变量,用来保存流程编号

2.5 拖放一个Assign 和WriteLine到PickBranch的Action中。(作为响应,我们只是随机产生一个流程编号,然后输出它)

这个设计过程,将得到下面这样的xaml.我这里就不一一截图了。如果觉得有必要,直接将这段xaml覆盖掉你的设计即可看到效果。

<Activity mc:Ignorable="sap" x:Class="DocumentReviewLib.DocumentReviewWorkflow" sap:VirtualizedContainerService.HintSize="470,711" mva:VisualBasic.Settings="Assembly references and imported namespaces for internal implementation" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mv="clr-namespace:Microsoft.VisualBasic;assembly=System" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:p="http://schemas.microsoft.com/netfx/2009/xaml/servicemodel" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:s1="clr-namespace:System;assembly=System" xmlns:s2="clr-namespace:System;assembly=System.Xml" xmlns:s3="clr-namespace:System;assembly=System.Core" xmlns:s4="clr-namespace:System;assembly=System.ServiceModel" xmlns:sa="clr-namespace:System.Activities;assembly=System.Activities" xmlns:sad="clr-namespace:System.Activities.Debugger;assembly=System.Activities" xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:scg="clr-namespace:System.Collections.Generic;assembly=System" xmlns:scg1="clr-namespace:System.Collections.Generic;assembly=System.ServiceModel" xmlns:scg2="clr-namespace:System.Collections.Generic;assembly=System.Core" xmlns:scg3="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:sd="clr-namespace:System.Data;assembly=System.Data" xmlns:sl="clr-namespace:System.Linq;assembly=System.Core" xmlns:st="clr-namespace:System.Text;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><Sequence DisplayName="文档审批流程" sad:XamlDebuggerXmlReader.FileName="d:\temp\WF4EventDrivenSolution\DocumentReviewLib\DocumentReviewWorkflow.xaml" sap:VirtualizedContainerService.HintSize="430,671"><Sequence.Variables><Variable x:TypeArguments="x:Int32" Default="-1" Name="TicketId" /></Sequence.Variables><sap:WorkflowViewStateService.ViewState><scg3:Dictionary x:TypeArguments="x:String, x:Object"><x:Boolean x:Key="IsExpanded">True</x:Boolean></scg3:Dictionary></sap:WorkflowViewStateService.ViewState><Pick DisplayName="文档创建" sap:VirtualizedContainerService.HintSize="408,547"><PickBranch DisplayName="用户提交了一个新的流程" sap:VirtualizedContainerService.HintSize="294,501"><PickBranch.Trigger><p:Receive DisplayName="收到用户的消息" sap:VirtualizedContainerService.HintSize="264,100" OperationName="CreateTicket" ServiceContractName="IDocumentReview" /></PickBranch.Trigger><Sequence DisplayName="事件响应" sap:VirtualizedContainerService.HintSize="264,283"><sap:WorkflowViewStateService.ViewState><scg3:Dictionary x:TypeArguments="x:String, x:Object"><x:Boolean x:Key="IsExpanded">True</x:Boolean></scg3:Dictionary></sap:WorkflowViewStateService.ViewState><Assign DisplayName="随机产生一个流程编号" sap:VirtualizedContainerService.HintSize="242,58"><Assign.To><OutArgument x:TypeArguments="x:Int32">[TicketId]</OutArgument></Assign.To><Assign.Value><InArgument x:TypeArguments="x:Int32">[New Random().Next()]</InArgument></Assign.Value></Assign><WriteLine DisplayName="输出信息" sap:VirtualizedContainerService.HintSize="242,61" Text="[&quot;流程被创建,编号为:&quot; &amp; TicketId]" /></Sequence></PickBranch></Pick></Sequence>
</Activity>

3. 设计一个宿主程序

流程设计好之后,我们如何将它托管起来,并且允许客户端发出流程有关的操作呢

我们需要设计一个宿主。为简便起见,我们可以用Console来做为宿主。

同时,我们需要添加几个程序集的引用

然后,修改Main方法,代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Activities;
using System.ServiceModel.Activities;
using System.ServiceModel.Description;namespace Host
{class Program{static void Main(string[] args){var host = new WorkflowServiceHost(new DocumentReviewLib.DocumentReviewWorkflow(),new Uri("http://localhost:8080/DRS"));host.AddDefaultEndpoints();host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });host.AddServiceEndpoint("IMetadataExchange",MetadataExchangeBindings.CreateMexHttpBinding(),"mex");host.Open();Console.WriteLine("Server is ready.");Console.Read();}}
}

这里,我们创建了一个所谓的WorkflowServiceHost(其实这是一个特殊的WCF中的ServiceHost对象),启动了在8080端口的监听

启动Host的调试(如果是Vista或Win7),请将Visual Studio run as administrator,如果没有出什么意外的话,你可以看到下面这样的一个窗口

然后,你在浏览器中输入下面的地址,可以看到

这就表示服务已经成功启动了。

根据上面页面的提示,我们可以产生一个服务代理类。注意,要使用Visual Studio Command Prompt,而不是默认的cmd

4. 设计一个客户端程序

我们接下来要设计一个Windows Forms的客户端程序,来使用该服务,发起流程的操作

将刚才工具所生成的两个文件添加到当前项目,并且将output.config修改为app.config

添加对System.ServiceModel的引用

修改窗口的设计如下

代码如下

        private void btCreate_Click(object sender, EventArgs e){var proxy = new DocumentReviewClient();proxy.CreateTicket();}

是不是很简单呢?其实这里就是WCF调用。(有在WF3中做过流程开发的朋友一定不会陌生,以前我们也是自己定义WCF服务来实现客户端与服务器端的通讯)

那么,现在可以进行调试了。

5. 调试流程

因为既要启动服务器,又要启动客户端,所以我们可以设置一下启动项目顺序

接下来,就可以按下F5键进行调试了

然后,我们点击“创建流程”按钮。我们并没有发现什么。流程并没有被启动。这是为什么呢?

放心,没有什么大不了的,这只是我预先设计好的一个“陷阱”而已。我是想让大家明白,所有的WCF调用,默认都不会启动流程,除非有一个属性设置为true。这就是那个Receive Activity的CanCreateInstance属性,我们可以回到DocumentReviewLib中,将其设置为true。因为它默认是false。这个属性的意思是说,如果收到这个事件,那么要不要创建工作流的实例。一般在一个流程的顶部事件中,都是设置为true的

再次按下F5键,进行调试,然后点击“创建流程”按钮

这样看起来,流程确实被创建了。如果我们点击多次呢?毫无疑问,它会有多个实例产生出来

看起来不错,不是吗?我们从客户端发起了一个操作,它将被工作流收到消息,并且进行了相应的处理。(我们这里只是输出消息,你可以想象一下,你完全可以在这里更新数据库的记录)

那么,接下来有一个话题,一般情况下,我们一个流程都不止一个事件,例如发起流程之后,需要将这个编号返回给客户端,同时还要等待经理审批。如此这般的需求怎么实现呢?

我在下一篇先介绍一下,如何将处理结果发回给客户端。

然后,第三篇介绍如何实现审批事件。

【备注】本文代码,可以通过 这里 下载

[转]WF事件驱动(1)相关推荐

  1. [转]WF事件驱动(4) -持久化

    本文转自:http://www.cnblogs.com/Mayvar/archive/2011/09/03/wanghonghua201109030451.html 前面三篇,我介绍到了如何在WF 4 ...

  2. WF(Windows Workflow Foundation)

    0         前言 各位网友大家好!Vista的到来对于跟随微软的开发人员可谓又是一场技术的革命,革命尚未到来已经就有了山雨欲来风满楼的感觉.从去年的Avalon(WPF开发代号).Indigo ...

  3. 由Node.js事件驱动模型引发的思考

    引言 近段时间听说了Node.js,很多文章表述这个事件驱动模型多么多么优秀,应用在服务器开发中有很大的优势,本身对此十分感性去,决定深入了解一下,由此也引发了一些对程序设计的思考,记录下来. 什么是 ...

  4. 微服务系列(五):事件驱动的数据管理

    编者的话|本文来自 Nginx 官方博客,是「Chris Richardson 微服务」系列的第五篇文章.第一篇文章介绍了微服务架构模式,并且讨论了使用微服务的优缺点:第二和第三篇描述了微服务架构模块 ...

  5. Python之路-python(Queue队列、进程、Gevent协程、Select\Poll\Epoll异步IO与事件驱动)

    一.进程: 1.语法 2.进程间通讯 3.进程池 二.Gevent协程 三.Select\Poll\Epoll异步IO与事件驱动 一.进程: 1.语法 1 简单的启动线程语法 2 def run(na ...

  6. 【讨论】基于WF的流程结构

    大家都知道,在WF中默认情况下,其活动是以树状结构组成的,简单说就是复合活动包含其子活动,如果子活动也是复合活动也可以包含其子活动,但同一个活动不能成为两个活动的子活动.这种方式被大量使用在WF自带的 ...

  7. golang-实现自己的事件驱动

    golang实现自己的事件驱动 众所周知,go中的异步操作都已经封装在了运行时的过程中,有关socket的网络的异步操作都封装到了go的netpoll中,从而简化了编程形式.本文也就根据evio库总结 ...

  8. [公告]欢迎您加入WF技术研究团队

    Microsoft Windows Workflow Foundation (WWF) 是一个可扩展框架,用于在 Windows 平台上开发工作流解决方案.Windows Workflow Found ...

  9. 网关技术选型,为什么选择 Openresty ?事件驱动、协程...

    今天跟大家聊下关于网关的话题 互联网公司,不论体量大小如何,其内部的技术架构基本都是相似的,体现在以下几个方面: 数据量过大,如何定制化存储 访问量高了,如何集群化部署,流量负载均衡 响应速度慢了,如 ...

最新文章

  1. Java学习从入门到精通
  2. 《浪潮之巅》--百年帝国读后感
  3. cla c 语言编译器,第九章 CLA_C2000_C_Compiler.pdf
  4. C++虚继承中构造函数和析构函数顺序问题以及原理
  5. 性能测试工具SilkPerformer介绍
  6. java 线程一起画图_java 多线程画图 不显示过程
  7. kaggle房价预测特征意思_Kaggle之预测房价
  8. Linux内核相关书籍
  9. LHF Objective-C语法(7)id类型、动态判断与选择器
  10. 21.Phabricator 安装
  11. 【优化算法】灰狼混合布谷鸟优化算法(GWO_CS)【含Matlab源码 1468期】
  12. 常见的web前端性能优化方法总结
  13. 华为NCE网管配置EPLAN
  14. 最全卡尔曼滤波原理简介
  15. [玩转UE4/UE5动画系统>技能系统(GAS)篇] 二 技能 Gameplay Ability(GA)
  16. 如何检测android手机是否支持3g网络或者4g网络 源码,iOS中怎么判断当前网络环境是2G/3G/4G/5G/WiFi...
  17. iOS依赖注入框架系列(一):介绍Typhoon
  18. 初等数学I 自然数 第二节 序数理论基础与自然数的运算
  19. 【转】立方体的体对角线穿过多少个正方体?
  20. 请问怎样取三位数的百位数,个位数,和十位数呢 (拆分)?

热门文章

  1. 【明哥报错簿】之 mybatis异常invalid comparison: java.util.Date and java.lang.String
  2. Python机器学习笔记:异常点检测算法——Isolation Forest
  3. 这四行棘手的C代码背后的概念
  4. 如何检查PHP数组是关联数组还是顺序数组?
  5. Freemarker生成静态化文件
  6. ssm项目配置文件中的包扫描bean,排除特定bean的扫描
  7. mysql的备份与恢复_实验十一 MySQLl备份与恢复1
  8. securecrt遇到一个致命的错误且必须关闭_高性能服务器之路 | 浅谈 Valgrind 内存错误检查神器 Memcheck...
  9. 使用Maven前夕(Maven项目架构管理工具、配置环境变量、阿里云镜像、本地仓库)
  10. 微信小程序文本溢出省略号(···)