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

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

转载于:https://blog.51cto.com/daizhj/339024

DinnerNow中的Work Flow应用(下) --- 订单流程相关推荐

  1. DinnerNow中的ASP.NET Ajax Extensions应用---选餐流程

    我们大概了解了一些关于DinnerNow的基本项目结构,以及其中比较主要的两个解决方案文件.接下来我会继续以实际网上选餐流程来说明关于DinnerNow中的ASP.NET Ajax Extension ...

  2. ASP.NET MVC3书店--第九节 注册与下订单(第一部分)(转)

    http://blog.sina.com.cn/s/blog_6ad539a90100rag1.html 在本节中,我们将要创建一个下订单控制器,该控制器将获取购买者的收货地址与付款信息.在下订单之前 ...

  3. 订单表的字段类型 mysql_Mysql数据库下订单表如何设计?

    Mysql数据库下订单表如何设计 商品表和订单表 . 通过一个表来关联. 那删除了商品,相关联的订单表如何显示出这个已经删除的商品 订单表需要冗余商品名.商品编号.价格等基本信息. 不能只保存一个商品 ...

  4. 小程序中商家入驻提醒、新订单提醒

    1. 应用场景 ThinkPHP技术QQ群: 828567087 用户在小程序商城购买下单之后,商家如何能及时收到新订单提醒,进行发货处理呢? 用户在小程序中申请入驻商家后,平台管理员如何能及时收到入 ...

  5. SAP 生产订单/流程订单中日期的解释

    SAP 生产订单/流程订单中日期的解释 基本开始日期:表示订单的开始日期 基本完成日期:表示订单的完成日期 我们在输入基本开始日期和基本完成日期时需要关注 调度 下面的"类型",其 ...

  6. 小米商场系统的购物网站的具体的功能实现(重点讲下订单的生成)

    小米商场系统的购物网站的具体的功能实现(重点讲下订单的生成) 一.需求分析 对于一个常用的购物网站,无非就是前台和后台,前台有相对于的注册和登录功能,负责数据的展示还有其购物车的生成,订单的生成,支付 ...

  7. 微信小程序支付返回签名错误_java 微信小程序微信支付统一下订单及数字签名错误问题(后端)...

    今天来分享一下之前做微信小程序微信支付遇到的一些坑,博主这里是微信小程序支付功能,因此选择的微信支付方式是JSAPI支付方式(温馨提示左下角有音乐哦). 首先我们肯定是要在小程序后台绑定一个商户号的, ...

  8. Google DFP广告管理系统简介:下订单

    您将要创造的 这是有关DFP广告管理系统的系列教程中的第四篇. 对于初学者来说,DFP广告管理系统似乎非常复杂,因此本系列旨在简化它. 如果您拥有一个或两个网站访问量适中的网站,并且想知道如何最好地产 ...

  9. Oracle EBS 中退货订单流程的系统操作记录

    Oracle EBS 中退货订单流程的系统操作记录 1.创建退货订单并登记,行状态为等待退货. 订单行项目 2.库存职责下,接收退货,输入RMA编号(退货订单编号)2015010008,点击查找,输入 ...

最新文章

  1. qrcode生产带logo_“白板”口罩打上LOGO装名牌 警方重拳出击清市场
  2. mysql client pip_mac pip install mysqlclient 报错
  3. LINUX设备驱动之设备模型一--kobject
  4. C语言使用fopen的两点注意事项
  5. annotation java log_使用java注释来注入logger依赖关系
  6. [视频教程] ubuntu系统下安装最新版的MySQL
  7. 颜色空间,图像格式,彩色转灰度函数
  8. 百度SEOB2B/论坛/网站自动更新/发布程序
  9. 滨州智能dcs系统推荐_推荐一:智能变电站监控系统典型作业培训教材
  10. 关于C#打包部署文件夹问题
  11. ERROR streaming.StreamExecution:createConsumer(ConsumerStrategy.scala:63)
  12. Postfix:邮件系统常见错误代码解释
  13. 如何重命名Git标签?
  14. [Android] 图片JNI(C++\Java)高斯模糊 多线程
  15. postman调用webservice接口
  16. PE文件解析(1):Dos头与NT头
  17. 基于HSV颜色空间的辅助车牌定位
  18. 河南计算机教师资格证,河南教师资格证考试科目
  19. 手把手教学京东api接口全部操作过程
  20. 四川师范大学自然地理(2-地壳)90分以上版本

热门文章

  1. 使用tmpfs的好处
  2. Linux磁盘空间监控告警
  3. 零基础学习.NET平台和Csharp编程开发
  4. 在C#代码中应用Log4Net(四)在Winform和Web中捕获全局异常
  5. 剑指offer——面试题59:对称的二叉树
  6. [Ubuntu]在Ubuntu系统下连接远程Ubuntu服务器并传输文件:安装putty, filezilla
  7. react 生命周期函数
  8. 详解Python的装饰器
  9. 2018-04-26java实习面试记录
  10. FFMPEG关键结构体