zoukankan      html  css  js  c++  java
  • DinnerNow中的Work Flow应用(下) 订单流程

          做为微软最新技术应用的DEMO。dinnernow使用了: IIS7, ASP.NET Ajax Extensions, LINQ, WCF,
    WF,WPF,Windows PowerShell, Card Space以及 .NET Compact Framework. 本文将会继续订餐流程,
    来讨论关于WF(Windows Work Flow Foundation)状态机, 在"订单"这一应用场景中的设计思路:)

          继续上一篇中的关于SendActivity的讨论,目前已经完成了订单的创建工作,下面就是要激活该定单流程
    的时候了.首先请先双击打开ProcessOrder.xoml文件,找到里面的sendActivity1,它是一个SendActivity,
    在这个Activity上面击右键属性,如下图:
        


        图中的一个非常重要的属性就是ServiceOperationInfo, 它定义了要调用的服务,这里它的属性值为:
    DinnerNow.OrderProcess.IUpdateOrder.StartRestaurantOrder.而这个StartRestaurantOrder操作又
    是什么东西呢.看来我们还有必要再去检查一下DinnerNow.ServiceHost项目下的 web.config文件,发现如
    下服务配置节点:
     <service behaviorConfiguration="WorkflowHostBehavior" name="DinnerNow.OrderProcess.RestaurantOrderWorkflow">
            <endpoint address="" binding="wsHttpContextBinding" contract="DinnerNow.OrderProcess.IUpdateOrder" />
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
          </service>
        看来要去RestaurantOrderWorkflow中去找StartRestaurantOrder方法,而RestaurantOrderWorkflow.xoml
    本身是一个状态机工作流,如下图:

      注:如果大家对状态机工作流不清楚,可以参考这篇文章《WF编程》系列之34 - 基本活动:状态活动 

         其中黑线箭头就是定单的流转方向.我们在当前状态机上击右键属性,查看该状态机的设置属性如下图所示:

        

    其中的:
        InitialStateName代表初始状态,因为状态机必有一个初始状态,这里它的属性值为:  
                              RestaurantOrderWorkflowInitialState,
        CompletedStateName代表工作流的结束状态,这里的值为OrderComplete

        当然光看这些还是无法知道StartRestaurantOrder方法的定义,这时我们要在RestaurantOrderWorkflow.xoml
    上击右键,选择"打开方式",在弹出窗口中选择"XML 编辑器", 在XML中找到下面的节点信息:

    <StateActivity x:Name="RestaurantOrderWorkflowInitialState">
      
    <EventDrivenActivity x:Name="ReceiveRestaurantOrder">
       
    <ns0:ReceiveActivity x:Name="receiveOrder" CanCreateInstance="True">
        
    <ns0:ReceiveActivity.ServiceOperationInfo>
         
    <ns0:TypedOperationInfo Name="StartRestaurantOrder" ContractType="{x:Type DinnerNow.OrderProcess.IUpdateOrder}" />
        
    </ns0:ReceiveActivity.ServiceOperationInfo>
        
    <ns0:ReceiveActivity.ParameterBindings>
         
    <WorkflowParameterBinding ParameterName="order">
          
    <WorkflowParameterBinding.Value>
           
    <ActivityBind Name="RestaurantOrderWorkflow" Path="orderToProcess" />
          
    </WorkflowParameterBinding.Value>
         
    </WorkflowParameterBinding>
         
    <WorkflowParameterBinding ParameterName="context">
          
    <WorkflowParameterBinding.Value>
           
    <ActivityBind Name="RestaurantOrderWorkflow" Path="updateOrderStatusActivity4_conversation1" />
          
    </WorkflowParameterBinding.Value>
         
    </WorkflowParameterBinding>
        
    </ns0:ReceiveActivity.ParameterBindings>
        
    <!--<CodeActivity x:Name="codeActivity1" ExecuteCode="AcceptOrderCode" />-->
       
    </ns0:ReceiveActivity>
       
    <ns1:UpdateOrderStatusActivity orderStatus="{ActivityBind RestaurantOrderWorkflow,Path=orderStatus}" IncomingOrder="{ActivityBind RestaurantOrderWorkflow,Path=orderToProcess}" conversation="{ActivityBind RestaurantOrderWorkflow,Path=updateOrderStatusActivity4_conversation1}" x:Name="updateOrderStatusActivity4" />
       
    <SetStateActivity x:Name="setStateActivity3" TargetStateName="OrderCooking" />
      
    </EventDrivenActivity>
     
    </StateActivity>

     
    这里需要解释一下,上面代码的第一行就是我们看到的状态机的初始化活动的名称,即这个StateActivity就是初始
    化活动,而EventDrivenActivity x:Name="ReceiveRestaurantOrder"代表当发生ReceiveRestaurantOrder事件时即启动
    当前的状态活动并将当前的状态转换到下一个新的状态(即上面代码中的TargetStateName="OrderCooking"属性值).
    当然状态机本身是需要有实例来运行的,所以CanCreateInstance="True".

    接下来我们看到了下面这一行代码:
    <ns0:TypedOperationInfo Name="StartRestaurantOrder" ContractType="{x:Type DinnerNow.OrderProcess.IUpdateOrder}" />
    到这里,我们找到了StartRestaurantOrder方法的声明位置,那StartRestaurantOrder运行代码又在何处呢,其实
    我们可以从上面代码中找到如下一行代码:

    <ns1:UpdateOrderStatusActivity orderStatus="{ActivityBind RestaurantOrderWorkflow,Path=orderStatus}" IncomingOrder="{ActivityBind RestaurantOrderWorkflow,Path=orderToProcess}" conversation="{ActivityBind RestaurantOrderWorkflow,Path=updateOrderStatusActivity4_conversation1}" x:Name="updateOrderStatusActivity4" />

      它指示当前状态初始化后要执行UpdateOrderStatusActivity.
         注:该类位于Workflow\UpdateOrderStatusActivity.cs文件,见下面代码:

    public partial class UpdateOrderStatusActivity: Activity
    {
         
    public static DependencyProperty IncomingOrderProperty = DependencyProperty.Register("IncomingOrder"typeof(DinnerNow.Business.Data.RestaurantOrder), typeof(UpdateOrderStatusActivity));
         
    public static DependencyProperty orderStatusProperty = DependencyProperty.Register("orderStatus"typeof(System.String), typeof(UpdateOrderStatusActivity));
         
    public static DependencyProperty conversationProperty = DependencyProperty.Register("conversation"typeof(System.Collections.Generic.Dictionary<stringstring>), typeof(UpdateOrderStatusActivity));

         [BrowsableAttribute(
    true)]
         [CategoryAttribute(
    "Parameters")]
         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
         
    public System.Collections.Generic.Dictionary<string,string> conversation
         {
             
    get
             {
                 
    return ((System.Collections.Generic.Dictionary<string,string>)(base.GetValue(UpdateOrderStatusActivity.conversationProperty)));
             }
             
    set
             {
                 
    base.SetValue(UpdateOrderStatusActivity.conversationProperty, value);
             }
         }


         [Description(
    "Restaurant Order")]
         [Browsable(
    true)]
         [Category(
    "Order")]
         [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
         
    public DinnerNow.Business.Data.RestaurantOrder IncomingOrder
         {
             
    get
             {
                 
    return ((DinnerNow.Business.Data.RestaurantOrder)(base.GetValue(UpdateOrderStatusActivity.IncomingOrderProperty)));
             }
             
    set
             {
                 
    base.SetValue(UpdateOrderStatusActivity.IncomingOrderProperty, value);
             }
         }
         [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
         [BrowsableAttribute(
    true)]
         [CategoryAttribute(
    "Parameters")]
         
    public string orderStatus
         {
             
    get
             {
                 
    return ((System.String)(base.GetValue(UpdateOrderStatusActivity.orderStatusProperty)));
             }
             
    set
             {
                 
    base.SetValue(UpdateOrderStatusActivity.orderStatusProperty, value);
             }
         }

        
    public UpdateOrderStatusActivity()
           {
                InitializeComponent();
           }

            
    protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
            {
                OrderService service 
    = new OrderService();
                service.UpdateOrderStatus(IncomingOrder, orderStatus, 
    this.WorkflowInstanceId);
                
    return ActivityExecutionStatus.Closed;
            }
    }

      上面代码中的conversation属性和IncomingOrder属性所绑定的就是我在上一篇文章所说的SendActivity所发
    送过来的参数,其中conversation就是那个Context上下文.当然这里还有一个属性orderStatus,其实它的属性值
    是在相应的StateActivity中已被设置好了.以当前的"RestaurantOrderWorkflowInitialState"为例,其属性值为:

    orderStatus="{ActivityBind RestaurantOrderWorkflow,Path=orderStatus}"

    其中的Path=orderStatus即指向RestaurantOrderWorkflow.xoml.cs文件中的属性声明:
    public static string orderStatus = "New Order";
         这样当运行上面的Execute方法之后,当前订单的状态就会更新为New Order.
      
         上面代码中的UpdateOrderStatus方法声明如下:
     

    public bool UpdateOrderStatus(DinnerNow.Business.Data.RestaurantOrder restaurantOrder, string status, Guid workflowId)
    {
        
    using (Business.OrderProcessing op = new DinnerNow.Business.OrderProcessing())
        {
            
    return op.UpdateOrderStatus(restaurantOrder, status, workflowId);
        }
    }

        上面代码中的op.UpdateOrderStatus会执行下面的LINQ语句:   

    public bool UpdateOrderStatus(DinnerNow.Business.Data.RestaurantOrder restaurantOrder, string status, Guid WorkflowId)
    {
        var orderItems 
    = from od in db.OrderDetails
                         
    where od.RestaurantId == restaurantOrder.RestaurantId
                         
    && od.OrderId == restaurantOrder.OrderId
                         select 
    new
                         {
                             OrderDetailId 
    = od.OrderDetailId
                         };

        
    foreach (var orderItemId in orderItems)
        {
            var orderItem 
    = db.OrderDetails.Single(oi => oi.OrderDetailId == orderItemId.OrderDetailId);
            orderItem.WorkflowId 
    = WorkflowId;
            orderItem.Status 
    = status;
            orderItem.StatusUpdatedTime 
    = DateTime.Now;
        }
        db.SubmitChanges();

        
    return true;
    }


      当然定单的状态会按图中所标记的箭头方向转向到下一个状态,在XML中可以在当前的StateActivity
    节点中找到下面的内容:
    <SetStateActivity x:Name="setStateActivity3" TargetStateName="OrderCooking" />

      其中的TargetStateName属性值即是下一个状态名称,其内容如下:

    <StateActivity x:Name="OrderCooking">
      
    <StateInitializationActivity x:Name="orderCookingInitialization">
       
    <ns1:UpdateOrderStatusActivity orderStatus="{ActivityBind RestaurantOrderWorkflow,Path=orderStatusCooking}" IncomingOrder="{ActivityBind RestaurantOrderWorkflow,Path=orderToProcess}" conversation="{x:Null}" x:Name="UpdateOrderStatusActivity1" />
      
    </StateInitializationActivity>
      
    <EventDrivenActivity x:Name="OrderCooked">
       
    <ns0:ReceiveActivity x:Name="receiveOrderReadyForPickup">
        
    <ns0:ReceiveActivity.ServiceOperationInfo>
         
    <ns0:TypedOperationInfo Name="OrderReadyForPickup" ContractType="{x:Type DinnerNow.OrderProcess.IUpdateOrder}" />
        
    </ns0:ReceiveActivity.ServiceOperationInfo>
        
    <ns0:ReceiveActivity.ParameterBindings>
         
    <WorkflowParameterBinding ParameterName="order">
          
    <WorkflowParameterBinding.Value>
           
    <ActivityBind Name="RestaurantOrderWorkflow" Path="orderToProcess" />
          
    </WorkflowParameterBinding.Value>
         
    </WorkflowParameterBinding>
        
    </ns0:ReceiveActivity.ParameterBindings>
       
    </ns0:ReceiveActivity>
       
    <SetStateActivity x:Name="setStateOrderReadyForPickup" TargetStateName="OrderReadyForPickup" />
      
    </EventDrivenActivity>
     
    </StateActivity>

      在这里, 我们看到了与刚才的初始化状态相类似的状态节点配置,并且它也有TargetStateName,其属性值
    为OrderReadyForPickup,看到这里感觉状态机越来越像是一个链表,它指定着状态传递的方向.而最终的完成
    状态就是状态机工作流中的CompletedStateName属性值.上面的状态活动的EventDrivenActivity为:
    OrderReadyForPickup,而这个驱动事件又是那个请求发出的呢?这里我们需要再打开另外一个解决方案,
    它位于安装目录下\solution\DinnerNow - Kiosk\solution\DinnerNow - Kiosk.sln, 我们编译这个WPF项目,
    得到下面的运行截图:

           

    当我们选取其中的一个定单之后,显示该订单的一些详细信息如下图:

      

    我们在这里通过下拉框更新了当前订单的状态,其最终的C#运行代码如下(OrderStatusWindow.xaml.cs):
       


       ..

       
    switch (newStatusText.Trim())
      
    {
          
    case "New Order":
              
    // we need to get the selected order
              
    // do nothing
              break;
          
    case "Ready for pickup":
              orderUpdateClient.OrderReadyForPickup(
    new RestaurantOrder() { OrderId = new Guid(this.SelectedOrder.OrderIdentifier), RestaurantId = this.SelectedOrder.RestaurantId });
              
    break;
          
    case "Out for Delivery":
              orderUpdateClient.OrderPickedUp(
    new RestaurantOrder() { OrderId = new Guid(this.SelectedOrder.OrderIdentifier), RestaurantId = this.SelectedOrder.RestaurantId }, Guid.NewGuid());
              
    break;
          
    case "Delivered":
              orderUpdateClient.OrderDelivered(
    new RestaurantOrder() { OrderId = new Guid(this.SelectedOrder.OrderIdentifier), RestaurantId = this.SelectedOrder.RestaurantId });
              
    break;
      }
    ;

      这里,还有一点需要说明的是在OrderDelivered这个StateActivity中的一些信息,因为在这个状态活动中
    有对上一篇文章中所说的ProcessOrder(顺序工作流)的信息发送,请看下面代码段:

    <StateActivity x:Name="OrderDelivered">
      
    <StateInitializationActivity x:Name="OrderDeliveredInitialization">
       
    <ns1:UpdateOrderStatusActivity orderStatus="{ActivityBind RestaurantOrderWorkflow,Path=orderStatusOrderDelivered}" IncomingOrder="{ActivityBind RestaurantOrderWorkflow,Path=orderToProcess}" conversation="{x:Null}" x:Name="updateOrderStatusActivity5" />
       
    <ns0:SendActivity x:Name="restaurantOrderComplete">
        
    <ns0:SendActivity.ServiceOperationInfo>
         
    <ns0:TypedOperationInfo Name="RestaurantOrderComplete" ContractType="{x:Type DinnerNow.OrderProcess.IProcessOrder}" />
        
    </ns0:SendActivity.ServiceOperationInfo>
        
    <ns0:SendActivity.ChannelToken>
         
    <ns0:ChannelToken Name="completeOrderToken" EndpointName="WSHttpContextBinding_IProcessOrder" />
        
    </ns0:SendActivity.ChannelToken>
       
    </ns0:SendActivity>
       
    <SetStateActivity x:Name="setStateActivity4" TargetStateName="OrderComplete" />
      
    </StateInitializationActivity>
     
    </StateActivity>

       其中下面这一行就是要使用的ProcessOrder工作流中的操作信息:
        <ns0:TypedOperationInfo Name="RestaurantOrderComplete" ContractType="{x:Type DinnerNow.OrderProcess.IProcessOrder}" />

    这样就会将ProcessOrder流程走完了.并且定单的状态也会变为OrderComplete。

         好了,订单状态的更新流转已介绍的差不多了,不过这里还有一个功能没有介绍,那就是DinnerNow提供了
    Window Mobile接收编辑发送功能,而这个功能也是订单流程的一部分,但这块内容与当前本文所讨论的技术没
    太大关联性.还是留到以后有时间再与大家聊一聊吧.

      好的,今天的内容就先告一段落,大家如果有兴趣,欢迎在回复中进行讨论:)

  • 相关阅读:
    关于let 和 var 的作用域问题
    ES6数组新增方法总结
    Javascript中的async await
    Promise个人笔记---【Promise的前世今生】
    KEIL, MDK 关于C99结构体变量初始化
    C89,C99: C数组&结构体&联合体快速初始化
    13. DMA
    GPIO, AFIO
    27. USART, Universal synchronous asynchronous receiver transmitter
    中断控制及basepri 与 basepri_max
  • 原文地址:https://www.cnblogs.com/daizhj/p/1205668.html
Copyright © 2011-2022 走看看