用款申请流程源码(WCF+WF)

转载请注明出处: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程序,对它的特性不了解。突然拿起五个月前的程序来看,还真是有点简单了。

posted @ 2010-08-31 23:06  小师傅  阅读(515)  评论(0编辑  收藏  举报