作爲微軟最新技術應用的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接收編輯發送功能,而這個功能也是訂單流程的一部分,但這塊內容與當前本文所討論的技術沒太大關聯性.仍是留到之後有時間再與你們聊一聊吧.
好的,今天的內容就先告一段落,你們若是有興趣,歡迎在回覆中進行討論:)