WF 工作流(4)

在第一章,我已经向你展示了在工作流中怎么使用variables(变量)和arguments(参数)。跟编码类似,variables类似于类成员,而arguments类似于方法的参数。你已经在前三章使用过variables了,在这一章,我将向你展示怎样使用input(输入)、output(输出)arguments(参数)和arguments(参数)是怎么在workflow和宿主程序之间传递。

 

创建一个新的解决方案

创建一个新的Workflow Console Application,如图Figure4-1。命名这个项目为OrderProcess,同时命名这个解决方案为Chapter04

 

在这个项目中,你将定义一个货物订单,然后把订单传递到工作流中。工作流将会计算订单的费用,然后把费用返回给应用程序。

 

定义订单类

第一步是定义订单的数据类型。在Solution Explorer(解决方案)中右击项目,选择Add > Class,如图Figure4-2所示。

 

Add New Item对话框中(如图Figure4-3),选择Class模板(它应该已经被默认地选择了),指定它的名字为Order.cs,点击Add

 

Solution Explorer(解决方案窗口)应该如图Figure4-4所示。

 

Order类中输入以下代码:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace OrderProcess

{

    public class OrderItem

    {

        public int OrderItemID { get; set; }

        public int Quantity { get; set; }

        public string ItemCode { get; set; }

        public string Description { get; set; }

    }

 

    public class Order

    {

        public Order()

        {

            Items = new List<OrderItem>();

        }

 

        public int OrderID { get; set; }

        public string Description { get; set; }

        public decimal TotalWeight { get; set; }

        public string ShippingMethod { get; set; }

 

        public List<OrderItem> Items { get; set; }

    }

}

 

Order类包含了一些公有成员(OrderIDDescriptionTotalWeightShippingMethod)和一个OrderItem类的集合。工作流会根据这些内容区决定订单的金额。点击F6去生成解决方案。这会编译生成Order类,后面我们需要用到。

 

实现工作流

模板为我们创建了一个工作流文件,名叫Workflow1.xaml。在Solution Explorer中,右击Workflow1.xaml文件,选择Rename(重命名),如图Figure4-5所示。命名为OrderWF.xaml

 

你同样要打开OrderWF.xamlcode view。在第一行,修改Class属性为:

 

x:Class="OrderProcess.OrderWF"

 

定义Arguments

打开OrderWF.xaml文件(design(设计)视图)。现在定义输入输出arguments。点击工作流设计器的左下角的Argument按钮。设计器将会弹出以下窗口(如图Figure4-6)。

 

 

提示:你可能会想起第一章中定义variables时是需要指定范围的,范围可能是整个工作流或者是特定的一个活动(和他的子活动)。对于arguments,然而,是不需要定义范围的,因为它已经被默认的指定为对整个工作流有效,因为它是指定来传入传出数据到工作流的。因此,当定义argument是是没有Scope属性的。

 

点击Create Argument链接。输入NameOrderInfoDirectionIn。点击ArgumentType,将会弹出一个下拉菜单,如图Figure4-7所示。

 

选择最后一条(Browse for Types)。这时会弹出一个对话框,如图Figure4-8所示。

 

展开OrderProcess引用,选择Order类,点击OK

 

提示:如果在OrderProcess引用中看不见Order类,你必须生成解决方案。点击Cancel按钮,点击F6生成解决方案(这时有可能报错“The type or namespace name ‘Workflow1’ could not be found (are you missing a using directive or an assembly reference?)”)

,然后就可以设置Argument类型了。

 

再次点击Create Argument链接,创建另外一个argument。输入NameTotalAmountDirectionOutArgumentTypeDecimalDecimal类型不再下拉列表中,你需要导航到这个类型(正如第一个argument定义那样)。Decimal类型可以在mscorlib引用的System命名空间中找到。

 

提示:你不必通过浏览引用和命名空间来找你需要的类型。你可以直接在对话框的最上面输入类型的命名空间和类型。例如,需要Decimal类型,只是需要输入System.Decimal。实际上,你甚至不需要输入特定的命名空间,你只要输入Decimal,之后下面就会出现匹配的类型。

 

设计工作流

现在你可以设计活动来处理传进来的订单了。首先拖拉一个Sequence活动到工作流设计器上,然后拖放一个WriteLine活动到Sequence中。设置Text属性为“Order Received”。拖放一个Assign活动到WriteLine活动的下面。设置DisplayNameInitialize TotalTo属性为TotalAmountValue属性为0Assign的属性窗口如图Figure4-9所示。

 

这个活动简单的把TotalAmount初始化为0

 

Switch活动

Switch活动的工作原理跟C#中的switch类似。它允许你执行一系列的活动根据表达式的值。你将会使用Switch活动去验证ShippingMethod而去决定收费。在Toolbox中,Switch活动显示为Switch<T>。这意味着他是一个模板类和他可以操作不同的数据类型。拖放一个Switch活动到Assign活动的下面。你应该可以看到弹出了一个对话框,如图Figure4-10所示,这个对话框要求你指定数据类型。ShippingMethod是一个string类型,因此选择String类型。

 

点击OK。在属性窗口中,设置DisplayNameHandling ChargesHandling Charges活动应该如图Figure4-11所示。

 

Switch活动有一个Expression属性,一个default分支,和一些用户定义的分支。输入expressionOrderInfo.ShippingMethod。点击Add new case链接(在活动的底部)。输入case的值为NextDay。再次点击Add new case,输入case2ndDay。这时活动如图Figure4-12所示。

 

设计器显示了所有的case(分支),和其中一个分支是展开的,以便你可以看到这个分支中有哪些活动。你可以点击Add an activity链接展开一个收缩的分支。

 

Expression活动

到目前为止,你已经定义了NextDay2ndDay分支到Switch活动中,default分支是用来当ShippingMethod不是其中一个分支的值时来执行的。现在你需要指定每一个分支要执行的活动。对于这个项目,你将使用Add活动。

 

提示:在System.Activities.Expressions命名空间中包含了很多工作流活动。包括AddSubtractMultiplyDivide活动。它也包括EqualGreaterThanAndOr活动,你可以直接在你的工作流中使用它们。

 

不幸运的是,Add活动不能在toolbox中,你需要直接在.xaml代码中输入这个活动。保存项目。在Solution Explorer中右击OrderWF.xaml,选择View Code。它会弹出一个窗口询问你是否关闭现有窗口,点击YesSwitch活动定义如下:

 

    <Switch x:TypeArguments="x:String" DisplayName="Handling Charges" Expression="[OrderInfo.ShippingMethod]" sap:VirtualizedContainerService.HintSize="481,260">

      <x:Null x:Key="NextDay" />

      <x:Null x:Key="2ndDay" />

    </Switch>

 

注意到NextDay2ndDay分支的属性x:Null,这表明这个分支没有包含活动,用以下代码覆盖这两行代码:

 

<Add x:TypeArguments="x:Decimal, x:Decimal, x:Decimal" x:Key="NextDay" sap:VirtualizedContainerService.HintSize="461,100" Left="[TotalAmount]" Result="[TotalAmount]" Right="[15]" />

      <Add x:TypeArguments="x:Decimal, x:Decimal, x:Decimal" x:Key="2ndDay" sap:VirtualizedContainerService.HintSize="461,100" Left="[TotalAmount]" Result="[TotalAmount]" Right="[10]" />

 

在上面代码之前加上:

 

      <Switch.Default>

        <Add x:TypeArguments="x:Decimal, x:Decimal, x:Decimal" sap:VirtualizedContainerService.HintSize="463,100" Left="[TotalAmount]" Result="[TotalAmount]" Right="[5]" />

      </Switch.Default>

 

注意:以上代码跟原文有不一致,因为我试过原文的方法会报错,所以我直接把Add活动添加到toolbox中,然后拖放到相应的分支中,上面的代码是自动生成的。

 

Add活动有三个属性:LeftRightResultRight的值加上Left的值然后赋值给Result属性,LeftResult属性设置为TotalAmount参数,Right属性被指定为一个常数。

 

Switch活动的完整定义如下:

 

    <Switch x:TypeArguments="x:String" DisplayName="Handling Charges" Expression="[OrderInfo.ShippingMethod]" sap:VirtualizedContainerService.HintSize="481,258">

      <Switch.Default>

        <Add x:TypeArguments="x:Decimal, x:Decimal, x:Decimal" sap:VirtualizedContainerService.HintSize="463,100" Left="[TotalAmount]" Result="[TotalAmount]" Right="[5]" />

      </Switch.Default>

      <Add x:TypeArguments="x:Decimal, x:Decimal, x:Decimal" x:Key="NextDay" sap:VirtualizedContainerService.HintSize="461,100" Left="[TotalAmount]" Result="[TotalAmount]" Right="[15]" />

      <Add x:TypeArguments="x:Decimal, x:Decimal, x:Decimal" x:Key="2ndDay" sap:VirtualizedContainerService.HintSize="461,100" Left="[TotalAmount]" Result="[TotalAmount]" Right="[10]" />

    </Switch>

 

保存项目,右击OrderWF.xaml文件,选择View DesignerSwitch活动如图Figure4-13所示。

 

ShippingMethodNextDay是,TotalAmount将会增加15$;当为2ndDay是,将增加10$。当为其他值时,将增加5$。如果你点击其中的一个switch分支,你将看到分支中包含什么活动,如图Figure4-14所示。

 

点击Add活动,它的属性将在属性窗口出现(如图Figure4-15所示),如果需要,你可以在这里直接修改它的属性。

 

拖放另外一个Assign活动到Switch活动的下面,DisplayNameFreight ChargesTo属性为TotalAmountValue属性为:

 

TotalAmount + (OrderInfo.TotalWeight * 0.50D)

 

拖放一个WriteLine活动到“Freight Charges”活动的下面,设置Text属性为:

 

“That Total amount is: $” + TotalAmount.ToString()

 

这展示了订单的总金额。最后的工作流图应该如图Figure4-16所示。

 

 

引用工作流

在这个项目,控制台程序自动的引用了工作流,用下面代码覆盖Program.cs

 

using System;

using System.Linq;

using System.Activities;

using System.Activities.Statements;

using System.Collections.Generic;

 

namespace OrderProcess

{

 

    class Program

    {

        static void Main(string[] args)

        {

            Order myOrder = new Order()

            {

                OrderID = 1,

                Description = "Need some stuff",

                ShippingMethod = "2ndDay",

                TotalWeight = 100

            };

 

            IDictionary<string, object> input = new Dictionary<string, object>

            {

                {"OrderInfo", myOrder}

            };

 

            //execute the workflow

            IDictionary<string,object> output = WorkflowInvoker.Invoke(new OrderWF(), input);

 

            //Get the TotalAmount returned by workflow

            decimal total = (decimal)output["TotalAmount"];

            Console.WriteLine("Workflow return ${0} for my order total", total);

 

            Console.WriteLine("Press ENTER to exit");

            Console.ReadLine();

        }

    }

}

 

首先创建了一个Order对象,在Order对象中定义了测试的数据。然后创建了一个Dictionary对象,用来存储Order对象。调用了WorkflowInvoker类的静态方法Invoke(),在Invoke()方法中传递了Dictionary对象。Invoke()方法创建和执行一个工作流实例在应用程序的线程中。

Dictionary中传递数据是可以同时传多个参数的。这是非常重要的去匹配dictionarykey和工作流中的传入参数,而且dictionary中对象的类型也要与工作流中传入参数匹配。

Invoke()方法返回一个Dictionary对象,这个dictionary对象包含了工作流的所有传出和传出/传入参数。

 

运行程序

点击F5运行程序,结果应该如下所示:

 

为了去验证结果是否正确,你可以手动的去计算一下。ShippingMethod2ndDay,这代表要加上$10TotalWeight100,价格为$0.50一磅,这就是一共$50。加上前面的$10就是$60

 

第四章源码下载

 

posted on 2011-08-02 08:50  Mayvar  阅读(894)  评论(0编辑  收藏  举报

导航