zoukankan      html  css  js  c++  java
  • WF事件驱动(3)

    前面两篇已经实现了最简单的基于事件的工作流程,用户可以在客户端(任意类型的客户端)发出流程操作的指令,通过WCF的通讯,驱动后台的工作流工作。 

    但之前的例子只有一个事件,就是“创建流程”的事件,显然这是不够的。这一篇就来把这个例子完善一下,通过这个练习之后,大家应该可以大致了解在WF4中如何设计基于事件的流程了

    我们将为这个流程添加一个“审批流程”的事件。

    本文代码,请通过 这里 下载

    1. 修改工作流设计

    很显然地,我们会在下面添加另外一个Pick Activity,然后里面也添加一个Receive来实现事件监听

    image

    同时,我们定义了三个变量用来接收用户传递过来的数据

    image

    我们在Receive上面设置了参数与这些变量之间的映射

    image

    一切看起来都还是挺自然的。请注意,这个Receive,因为是第二个事件,所以无需创建新的工作流实例(如果每个事件都创建新的实例,那就乱套了),也就是说CanCreateInstance不需要设置为true

    image

    这个工作流的xaml文件代码如下

    <Activity mc:Ignorable="sap" x:Class="DocumentReviewLib.DocumentReviewWorkflow" sap:VirtualizedContainerService.HintSize="483,1245" 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:ssa="clr-namespace:System.ServiceModel.Activities;assembly=System.ServiceModel.Activities" 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="443,1205">
        <Sequence.Variables>
          <Variable x:TypeArguments="x:Int32" Default="-1" Name="TicketId" />
          <Variable x:TypeArguments="p:CorrelationHandle" Name="__handle1" />
        </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="421,677">
          <PickBranch DisplayName="用户提交了一个新的流程" sap:VirtualizedContainerService.HintSize="307,631">
            <PickBranch.Trigger>
              <p:Receive x:Name="__ReferenceID0" CanCreateInstance="True" DisplayName="收到用户的消息" sap:VirtualizedContainerService.HintSize="277,100" OperationName="CreateTicket" ServiceContractName="IDocumentReview">
                <p:Receive.CorrelationInitializers>
                  <p:RequestReplyCorrelationInitializer CorrelationHandle="[__handle1]" />
                </p:Receive.CorrelationInitializers>
              </p:Receive>
            </PickBranch.Trigger>
            <Sequence DisplayName="事件响应" sap:VirtualizedContainerService.HintSize="277,413">
              <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="255,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>
              <p:SendReply Request="{x:Reference __ReferenceID0}" DisplayName="SendReplyTo收到用户的消息" sap:VirtualizedContainerService.HintSize="255,90">
                <p:SendMessageContent DeclaredMessageType="x:Int32">
                  <InArgument x:TypeArguments="x:Int32">[TicketId]</InArgument>
                </p:SendMessageContent>
              </p:SendReply>
              <WriteLine DisplayName="输出信息" sap:VirtualizedContainerService.HintSize="255,61" Text="[&quot;流程被创建,编号为:&quot; &amp; TicketId]" />
            </Sequence>
          </PickBranch>
        </Pick>
        <Pick DisplayName="文档审批" sap:VirtualizedContainerService.HintSize="421,364">
          <PickBranch DisplayName="经理审批" sap:VirtualizedContainerService.HintSize="285,318">
            <PickBranch.Variables>
              <Variable x:TypeArguments="x:String" Name="action" />
              <Variable x:TypeArguments="x:String" Name="comment" />
              <Variable x:TypeArguments="x:Int32" Name="reviewId" />
            </PickBranch.Variables>
            <PickBranch.Trigger>
              <p:Receive DisplayName="经理审批" sap:VirtualizedContainerService.HintSize="255,100" OperationName="UpdateTicket" ServiceContractName="IDocumentReview">
                <p:ReceiveParametersContent>
                  <OutArgument x:TypeArguments="x:String" x:Key="action">[action]</OutArgument>
                  <OutArgument x:TypeArguments="x:String" x:Key="comment">[comment]</OutArgument>
                  <OutArgument x:TypeArguments="x:Int32" x:Key="id">[reviewId]</OutArgument>
                </p:ReceiveParametersContent>
              </p:Receive>
            </PickBranch.Trigger>
            <WriteLine DisplayName="打印消息" sap:VirtualizedContainerService.HintSize="255,100" Text="[String.Format(&quot;Ticket update --- action : {0}, comment :{1} &quot;, action, comment)]" />
          </PickBranch>
        </Pick>
      </Sequence>
    </Activity>

    2. 更新客户端代码和设计

    工作流已经进行了修改,相应地,客户端代码也需要有一些更新

    启动宿主程序,然后通过下面的方式再次得到客户端代码文件。

    image

    同样,我们只需要添加那个DocumentReviewWorkflow.cs到项目中来

    我们修改窗口如下,添加审批的工具按钮

    image

    有关代码如下

            private void btApproval_Click(object sender, EventArgs e)
            {
                //同意某个流程
                var action = "approval";
                UpdateTicket(action);
    
            }
    
            private void UpdateTicket(string action)
            {
                if (lstTickets.SelectedIndex > -1)
                {
                    var id = int.Parse(lstTickets.SelectedItem.ToString());
                    var comment = txtComment.Text;
    
                    var proxy = new DocumentReviewClient();
                    proxy.UpdateTicket(action, comment, id);
    
                }
            }
    
            private void btReject_Click(object sender, EventArgs e)
            {
                var action = "Reject";
                UpdateTicket(action);
            }

    代码也是很好理解的,几乎无需特别的解释。

    3. 调试程序

    imageimage

    我几乎迫不及待地想要看一下效果了。选择某个Ticket之后,点击“同意”按钮,激动人心的时刻到了。晕,又是什么也没有发生。Angry smile

    你猜对了,这又是我预先埋下的一个“陷阱”。放心吧,如果那么容易做出来,我是不会专门写一篇来介绍的。

    4.修改流程设计

    有点沮丧吗?其实大可不必。淡定,淡定

    静下心来想一下,为什么它不能工作呢?很显然,客户端的请求是发送到了服务端的,那么就是说服务端不知道该怎么处理?这样解释是合理的吧。

    那么服务端为什么不知道该怎么处理呢?你可能会说,我都告诉他TicketId了,为什么它不会找到那个Instance,然后触发里面的事件呢?

    症结点就在于这里,你光是传递TicketId过来是不够的,还需要在工作流中将这个Id标识为一个上下文事件关联的依据。

    还记得第二篇提到的CorrelationHandler吗?既然事件需要被定位到,那么它就需要有一定的机制可以区分。不同实例之间就是通过不同的CorrelationHandler来区别的。

    那么,我们既然想让UpdateTicket事件与相应的CreateTicket事件所创建的那个实例关联起来,就需要定义一个特殊的Handler。

    image

    注意,这个__handler是我们定义的,而上面的那个__handler1是之前第二个练习时自动创建的

    接下来,我们需要对这个handler进行初始化。这个可以通过一个InitializeCorrelation的Activity来完成

    image

    我们让这个初始化好的handler具有一个属性,Id,保存当前这个TicketId

    image

    然后,我们修改”UpdateTicket”这个事件

    设置CorrelatesWith属性

    image

    设置CorrelatesOn属性

    image

    这个设置,也就是说,让UpdateTicket方法传入的id参数,自动作为查找CorrelationHandler的依据

    【注意】这一步至关重要

    保存流程,编译即可。因为这种更改,不涉及到与客户端的合约更改,所以无需重新生成客户端代码。这也是用WF来做流程设计的好处之一。

    最终版xaml如下

    <Activity mc:Ignorable="sap" x:Class="DocumentReviewLib.DocumentReviewWorkflow" sap:VirtualizedContainerService.HintSize="483,1378" 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:ssa="clr-namespace:System.ServiceModel.Activities;assembly=System.ServiceModel.Activities" xmlns:ssx="clr-namespace:System.ServiceModel.XamlIntegration;assembly=System.ServiceModel" 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="443,1338">
        <Sequence.Variables>
          <Variable x:TypeArguments="x:Int32" Default="-1" Name="TicketId" />
          <Variable x:TypeArguments="p:CorrelationHandle" Name="__handle1" />
          <Variable x:TypeArguments="p:CorrelationHandle" Name="__handler" />
        </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="421,810">
          <PickBranch DisplayName="用户提交了一个新的流程" sap:VirtualizedContainerService.HintSize="307,764">
            <PickBranch.Trigger>
              <p:Receive x:Name="__ReferenceID0" CanCreateInstance="True" DisplayName="收到用户的消息" sap:VirtualizedContainerService.HintSize="277,100" OperationName="CreateTicket" ServiceContractName="IDocumentReview">
                <p:Receive.CorrelationInitializers>
                  <p:RequestReplyCorrelationInitializer CorrelationHandle="[__handle1]" />
                </p:Receive.CorrelationInitializers>
              </p:Receive>
            </PickBranch.Trigger>
            <Sequence DisplayName="事件响应" sap:VirtualizedContainerService.HintSize="277,546">
              <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="255,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>
              <p:InitializeCorrelation Correlation="[__handler]" sap:VirtualizedContainerService.HintSize="255,93">
                <InArgument x:TypeArguments="x:String" x:Key="Id">[TicketId.ToString()]</InArgument>
              </p:InitializeCorrelation>
              <p:SendReply Request="{x:Reference __ReferenceID0}" DisplayName="SendReplyTo收到用户的消息" sap:VirtualizedContainerService.HintSize="255,90">
                <p:SendMessageContent DeclaredMessageType="x:Int32">
                  <InArgument x:TypeArguments="x:Int32">[TicketId]</InArgument>
                </p:SendMessageContent>
              </p:SendReply>
              <WriteLine DisplayName="输出信息" sap:VirtualizedContainerService.HintSize="255,61" Text="[&quot;流程被创建,编号为:&quot; &amp; TicketId]" />
            </Sequence>
          </PickBranch>
        </Pick>
        <Pick DisplayName="文档审批" sap:VirtualizedContainerService.HintSize="421,364">
          <PickBranch DisplayName="经理审批" sap:VirtualizedContainerService.HintSize="285,318">
            <PickBranch.Variables>
              <Variable x:TypeArguments="x:String" Name="action" />
              <Variable x:TypeArguments="x:String" Name="comment" />
              <Variable x:TypeArguments="x:Int32" Name="reviewId" />
            </PickBranch.Variables>
            <PickBranch.Trigger>
              <p:Receive CorrelatesWith="[__handler]" DisplayName="经理审批" sap:VirtualizedContainerService.HintSize="255,100" OperationName="UpdateTicket" ServiceContractName="IDocumentReview">
                <p:Receive.CorrelatesOn>
                  <p:XPathMessageQuery x:Key="Id">
                    <p:XPathMessageQuery.Namespaces>
                      <ssx:XPathMessageContextMarkup>
                        <x:String x:Key="xgSc">http://tempuri.org/</x:String>
                      </ssx:XPathMessageContextMarkup>
                    </p:XPathMessageQuery.Namespaces>sm:body()/xgSc:UpdateTicket/xgSc:id</p:XPathMessageQuery>
                </p:Receive.CorrelatesOn>
                <p:ReceiveParametersContent>
                  <OutArgument x:TypeArguments="x:String" x:Key="action">[action]</OutArgument>
                  <OutArgument x:TypeArguments="x:String" x:Key="comment">[comment]</OutArgument>
                  <OutArgument x:TypeArguments="x:Int32" x:Key="id">[reviewId]</OutArgument>
                </p:ReceiveParametersContent>
              </p:Receive>
            </PickBranch.Trigger>
            <WriteLine DisplayName="打印消息" sap:VirtualizedContainerService.HintSize="255,100" Text="[String.Format(&quot;Ticket update --- Ticketid :{0} , action : {1}, comment :{2} &quot;, reviewId, action, comment)]" />
          </PickBranch>
        </Pick>
      </Sequence>
    </Activity>

    5.测试程序

    发起流程后,分别选择不同的编号,进行审批。结果如下

    image

    大功告成,收工

    总结:

    利用WF 4的全新框架,可以快速设计基于事件驱动的工作流。它提高了开发效率,简化了开发流程,而更重要的是,这对于广大开发人员的身心健康有着很有益地帮助

  • 相关阅读:
    前端常用设计模式和工作中应用场景思考
    webpack从零开始打造react项目(更新中...)
    操作系统-进程
    go语言web框架-如何使用gin教程+react实现web项目
    JavaScript逗号运算符的用法
    react的生命周期和使用
    在Vue项目中使用wangEditor
    TypeScript实现axios
    SpringBoot整合邮件发送(thymeleaf和freemarker)
    SpringBoot整合RabbitMQ
  • 原文地址:https://www.cnblogs.com/Mayvar/p/wanghonghua201109030449.html
Copyright © 2011-2022 走看看