博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
DinnerNow中的Work Flow应用(下) --- 订单流程
阅读量:5752 次
发布时间:2019-06-18

本文共 11433 字,大约阅读时间需要 38 分钟。

    做为微软最新技术应用的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本身是一个状态机工作流,如下图:

  注:如果大家对状态机工作流不清楚,可以参考这篇文章 

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

    

其中的:
    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
<
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方法声明如下:
 

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

本文转自 daizhenjun 51CTO博客,原文链接:http://blog.51cto.com/daizhj/339024,如需转载请自行联系原作者

你可能感兴趣的文章
【SDN】Openflow协议中对LLDP算法的理解--如何判断非OF区域的存在
查看>>
纯DIV+CSS简单实现Tab选项卡左右切换效果
查看>>
栈(一)
查看>>
ios 自定义delegate(一)
查看>>
创建美国地区的appleId
查看>>
例题10-2 UVa12169 Disgruntled Judge(拓展欧几里德)
查看>>
JS 原生ajax写法
查看>>
Composer管理PHP依赖关系
查看>>
React.js学习笔记之JSX解读
查看>>
我所了解的Libevent和SEDA架构
查看>>
Socket编程问题小记
查看>>
基于Flask-Angular的项目组网架构与部署
查看>>
一张图道尽程序员的出路
查看>>
redis 常用命令
查看>>
LVS+Keepalived高可用负载均衡集群架构
查看>>
烂泥:kvm安装windows系统蓝屏
查看>>
iPhone开发面试题--葵花宝典
查看>>
EdbMails Convert EDB to PST
查看>>
POJ 2184
查看>>
大话 程序猿 眼里的 接口
查看>>