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)状态机, 在"订单"这一应用场景中的设计思路:)
html

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


    图中的一个很是重要的属性就是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自己是一个状态机工做流,以下图:
web

  注:若是你们对状态机工做流不清楚,能够参考这篇文章《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运行代码又在何处呢,其实咱们能够从上面代码中找到以下一行代码:
ide

< 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文件,见下面代码:
ui

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 < string string > ),  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方法声明以下:
 this

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语句:   spa

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接收编辑发送功能,而这个功能也是订单流程的一部分,但这块内容与当前本文所讨论的技术没太大关联性.仍是留到之后有时间再与你们聊一聊吧.

  好的,今天的内容就先告一段落,你们若是有兴趣,欢迎在回复中进行讨论:)