转载请注明出处:http://hi.baidu.com/wingingbob/blog/item/bfb2a4d34c84f2d8a8ec9a78.html
这是我在进入第一家公司的时候写的第一个WF程序。很简单,一个状态机的用款申请工作流程。当时比较遗憾的是没有实现多个结点的并行操作。不过让人高兴的,作为一个WCF+WF入门程序,还是审核通过了。
注意:源代码采用 .NET Framework 3.5
上面是流程图。触发申请状态后,写入申请信息到数据库,进入审核状态中的业务部审核状态。同理,按流程图示的连线方向,转向下一个结点,三个部门全部通过,进入完成状态;而当三个审核部门有一方审核失败,会通过“审核失败”EventDrivenActivity活动,返回申请状态,此时申请人可以重新填写申请表单或者放弃此次用款申请。
在WCF上,有一个服务器程序,负责流程的运转控制,而每个客户端可以通过自己的ID登录到服务器,履行自己的职责。
服务器配置文件如下:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="DBConnString" value="Data Source=.\SQLExpress;Initial Catalog=SpendingSample_wrb;Integrated Security=SSPI"/>
</appSettings>
<system.serviceModel>
<services>
<service name="SpendingSample.Service.SpendingService" behaviorConfiguration="serviceBehavior">
<endpoint address="SpendingSample"
binding="netTcpBinding" bindingConfiguration="" contract="SpendingSample.Service.ISpendingService" />
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:32010" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="serviceBehavior">
<serviceMetadata/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
客户端配置文件如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_ISpendingService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard" listenBacklog="10"
maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10"
maxReceivedMessageSize="65536">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://localhost:32010/SpendingSample"
binding="netTcpBinding" bindingConfiguration="NetTcpBinding_ISpendingService"
contract="ServiceReference1.ISpendingService" name="NetTcpBinding_ISpendingService">
<identity>
<userPrincipalName value="BOBWEI\think" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
* 可以将上面加粗字体改为自己的机器配置。客户端引用,需要服务器配置好并运行后,通过添加服务引用获得。具体的步骤,自己去找些WCF书籍,有两种简洁地添加客户服务引用的方法。
解决方案分为服务器、客户端以及服务器服务三个项目。
SpendingSample 是客户端程序,负责与服务器进行WCF交互,显示客户操作界面。
SpendingSample.Server 是服务器端程序,负责WCF服务和WF服务的承载,如下:
/// <summary>
/// 启动服务
/// </summary>
private void RunService()
{
this.wfRuntime = new WorkflowRuntime();
this.wfRuntime.WorkflowIdled += OnWorkflowIdled;
ExternalDataExchangeService dataExchangeService = new ExternalDataExchangeService();
this.wfRuntime.AddService(dataExchangeService);
SpendingWorkflowService spendingWorkflowService = new SpendingWorkflowService();
dataExchangeService.AddService(spendingWorkflowService);
SqlWorkflowPersistenceService sqlWfPersistenceService = new SqlWorkflowPersistenceService(DBOperation.ConnectionString, false,
new TimeSpan(1, 0, 0), new TimeSpan(0, 0, 1));
this.wfRuntime.AddService(sqlWfPersistenceService);
this.wfRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(OnWorkflowCompleted);
//this.wfRuntime.WorkflowAborted += new EventHandler<WorkflowEventArgs>(OnWorkflowAborted);
this.wfRuntime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(OnWorkflowTerminated);
this.wfRuntime.StartRuntime();
SpendingService.wfRuntime = this.wfRuntime;
this.serviceHost = new ServiceHost(typeof(SpendingService));
this.serviceHost.Open();
}
更具体的请查看Form1.cs代码源文件。
SpendingSample.Service 是WCF和WF核心程序,包含WCF服务和WF的运行接口和实现。
数据库只有五个用户表:
其中CompletedScope和InstanceState表是执行WF持久化服务,由.NET Framework SDK提供,导入创建的。它的sql文件在下面这个位置(中文版,假设你的系统目录是C:\Windows):
C:\WINDOWS\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\ZH-CHS
在日志最后的源代码下载里,已经包括了这些sql文件。
Spending表是程序主要的操作表。另外两个分别是用户表和部门表。
当然,数据库MDF文件已经被我拷贝到源码包里,直接将它们附加到你的SQL Server 2005里即可。
下面是一个运行演示:
(前提是已经将数据库MDF文件附加到数据库服务器中并可用,上面提到的两个配置文件配置正确。)
1.启动 SpendingSample.Server.exe(服务器端),然后点击“启动服务”按钮等服务已启动提示。
2.启动四个客户端窗口,分别登录不同的用户:
部门 用户名 口令(参见数据库中的Users表)
------ -------- ------
普通登录 Bob 1
业务部 aa aa
纪监审 bb bb
财务部 cc cc
3.登录成功后,Bob用户已经处于一个申请的队列当中,而且没有通过纪监审的审核。这是上次没有通过审核的用款申请,由于持久化的原因,此流程被完整地保存到数据库中,所以在Bob登录后,会立即看到上次申请所处的流程位置。
点击红色的“拒绝”链接,在出现的“审核状态”窗口里,可以重新填写申请表间,再次提交申请,或者放弃这次的申请。为了演示,我们点放弃,然后完成一个全部通过的申请过程。
4.在用款申请窗口中,点“填写新的用款申请”按钮,激活用款申请表单,添加一些数据,然后点“提交申请”。
这样,Bob就向服务器提交了一份用款申请。程序进入等待状态,当有部门作出回应后,通过WCF回调契约,刷新各部门的审核状态。
/// <summary>
/// 回调契约
/// (回调契约不必标记ServiceContract特性[《WCF服务编程》190页])
/// </summary>
public interface ICallback
{
[OperationContract(IsOneWay = true)]
void BillCallback(DataSet ds);
}
5.按照上面的流程图,业务部(aa用户)最先收到通知。
选中这个需要被审核的申请,点“审核”按钮,会出现业务部审核窗口,您可以选择“同意”或者“拒绝”,拒绝申请需要输入拒绝原因。
这里点“同意”,“确定”,通过业务部审核。
6.这时,按照流程的设计,会进入纪监审(bb用户)审核结点。用同样的方法审核通过。
在每次部门审核操作之后,你会注意到,Bob用户的申请窗口中会显示出相关的审核状态,也就是前面说的WCF回调功能。
另外,你也不必担心哪个部门没有上线,或者审核后下线了,因为这个WF实例加载了持久化服务,每当流程进入空闲状态时(也就是每个状态前后的等待事件触发或者流程开始和结束时),会自动将数据序列化到数据库里。
7. 同样,您可以让财务部也通过Bob的审核。当审核全部通过后,Bob又可以填写新的用款申请了。
8.我们“查看已完成的用款申请”。
高亮选中的是刚刚完成的申请。当然,我们可以查看它们的“详细”信息。
9.退出所有客户端,关闭服务器程序。
这只是一个简单的WCF+WF的例子,待完善的功能很多。麻雀虽小,五脏俱全,有兴趣的朋友可以拿去学习。在WCF回调功能的使用上有更好的方法实现,当时也是第一次做WCF程序,对它的特性不了解。突然拿起五个月前的程序来看,还真是有点简单了。