前六单请参见苦-咖啡:http://www.cnblogs.com/kukafeiso/category/250073.html
谢谢苦-咖啡的辛苦分享,偶这几天闲来没事,翻译了第7章出来,有不对的地方欢迎大家指正。
第七章 扩展内置的活动
在这一章,你将继续完善订单价格规则。在这过程中,我将使用两种技术来演示扩展内置的活动:自定义活动和InvokeMethod活动。
重新利用Chapter6项目
打开Visual Studio 2010 创建一个新的项目,选择Blank Solution模板,如图Figure7-1所示。输入解决方案名为Chapter7。
Figure 7-1. Creating a blank solution
复制Chapter06目录下的OrderProcess文件夹到Chapter07的目录下。在Solution Explorer中,右击Chapter07解决方案,选择Add>Existing Project。Add Existing Project对话框会出现。选择Chapter07/OrderProcess下的OrderProcess.csproj文件。(这跟上一章的做法是一样的。)
使用自定义活动
当前项目的对于所有项价格固定为$10,你可以创建自定义一个可以根据ItemCode属性查询item价格的自定义活动。打开Order.cs程序文件并添加如下类的定义,下面类定义的item属性将通过自定义活动返回。
//---------------------------------------------
// Define the structure returned by the
// LookupItem custom activity
//---------------------------------------------
public class ItemInfo
{
public string ItemCode { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
}
提醒:WF4.0不再允许在你的流程定义中添加任何可执行的代码,所有的代码只能在活动中执行。在以前的版本中,你可以使用CodeActivity类并且在代码旁类写你自己的代码,使用WF4.0,CodeActivity将不再是具体类了,而是大部分内置活动的基类且是抽象的类(可以作为自定义活动的基类),但它不能在workflow中直接使用了。这意味着在WF4.0中,你会发现你自己将要写很多的自定义活动。幸运的是,你将很容易的实现这些定自义活动的(接下来你将在本章中看到如何轻易地实现)。
实现一个自定义活动
在Solution Explorer中,右击OrderProcess项目选择Add->New Item,在新建Item对话框中选择Code Activity项,并输入类名为LookupItem.cs,如下图Figure 7-2
Figure 7-2. Creating a custom activity
Listing 7-1. LookupItem Class
using System;
using System.Activities;
namespace OrderProcess
{
public sealed class LookupItem : CodeActivity
{
public InArgument<string> ItemCode { get; set; }
public OutArgument<ItemInfo> Item { get; set; }
protected override void Execute(CodeActivityContext context)
{
ItemInfo i = new ItemInfo();
i.ItemCode = context.GetValue<string>(ItemCode);
switch (i.ItemCode)
{
case "12345":
i.Description = "Widget";
i.Price = (decimal)10.0;
break;
case "12346":
i.Description = "Gadget";
i.Price = (decimal)15.0;
break;
case "12347":
i.Description = "Super Gadget";
i.Price = (decimal)25.0;
break;
}
context.SetValue(this.Item, i);
}
}
}
正如工作流程有输入和输出参数一样(第四章已经解释过了),活动也有输入和输出参数。事实上,这些必须设置内置类的属性是活动的参数(比如WriteLine活动的Text属性)。你定义的自定义活动(LookupItem)具有ItemCode作为输入参数而返回ItemInfo作为输出参数。
LookupItem类继承自CodeActivity并重写了Execute方法,在方法中创建了ItemInfo并将输入参数ItemCode赋值给新创建的ItemInfo的实例属性ItemCode。提醒一下,方法中为什么使用Context.GetValue()取得ItemCode的值?这是因为ItemCode参数由工作流自身维护的,当Execute()方法被调用时CodeActivityContext将提供流程信息的设置和获取操作。
在Exectue()方法中,我们将Descript和Price属性设置为常量值是为了演示方便,在实际应用中,可以从数据库中查出值并赋给它们。最后程序调用了context.SetValue()方法将ItemInfo存储到输出参数中。
按F6重新生成程序。
使用LookupItem活动
切换OrderWF.xaml为视图设计模式,提醒:程序编译成功后会自动将LookupItem添加到Toolbox中。见Figure 7-3.
Figure 7-3. The LookupItem activity has been added to the Toolbox
展开” Accumulate Order Items”活动,将看到如下图所示的界面(Figure 7-4.):
Figure 7-4. The initial design of the Accumulate Order Items activity
Foreach遍历了OrderInfo.Items中的每一个item,并放置了将每个item的OrderTotal加了10$的Assign活动。接下来拖动Sequence活动到Body主体部分,并将DisplayName设置为”Lookup Item”,删除Assign活动。拖动”LookupItem”自定义活动到”Lookup Item”Sequence活动主体部分,”LookupItem”属性窗口包含了这个活动定义的ItemCode和Item参数。DisplayName参数是基类CodeActivity中已经定义的。
Figure 7-5. The Properties window of the LookupItem activity
在ItemCode属性中输入VB表达式:item.ItemCode。
接下你必须定义一个变量来存储ItemInfo类的返回值,点击流程设计器左下角的【变量】按钮,在显示的变量清单中点击【创建变量】将会添加一条变量记录在清单中,名称输入对应输入ItemDetails,双击变量类型会弹出下拉框,选中下拉项中的【浏览类型…】弹出类型选择框,如下图所示:
Figure 7-6. Selecting the class that defines the variable type
当前变量的默认作用域”Lookup Item”Seqence活动是正确的。变量定义结果如下图Figure 7-7.所示:
Figure 7-7. The definition for the ItemDetails variable
现在选择LookupItem活动并设置Item属性为ItemDetails。通过自定义活动”LookupItem”返回ItemInfo实例并存储到ItemDetails变量中。拖动Assign活动到LookupItem活动下方且放置在名为Lookup Item的Sequence活动块中。将Assign活动的两个属性To和Value分别设置为TotalAmount和TotalAmount + (item.Quantity * ItemDetails.Price)。这个表达式是计算所有订单的总金额。流程示例如下图Figure 7-8:
Figure 7-8. The final Accumulate(累积) Order Items activity
按F5运行程序,将会看图如下图所示的结果
Order Received
The item total is: $105
Item is out of stock - Item Code: 12346
The total amount is: $165
Workflow returned $165 for my order total
Press ENTER to exit
你可以按照下面所示通过手工计算来验证下结果是否正确!
InvokeMethod活动
InvokeMethod活动提供了除标准内置活动之外的另一种实现方式。你可以使用这种方式去回调某个类的方法。InvokeMethod活动类不需要作为工作流一部分或使用任何工作流的基类。在下面的示例项目中,您将实现一个基于各种规则为的类来计算折扣金额,工作流将回调此类来计算指定订单的折扣。
创建DisCount类
在Solution Explorer中,右击OrderProcess项目选择Add->Class将新建的类命名为OrderDiscount.cs,类的实现就像下面的代码所示:
Listing 7-2. OrderDiscount class
using System;
using System.Collections.Generic;
namespace OrderProcess
{
public static class OrderDiscount
{
public static decimal ComputeDiscount(Order o, decimal total)
{
// Count the number of items ordered
int count = 0;
foreach (OrderItem i in o.Items)
{
count += i.Quantity;
}
// Determine the discount percentage
decimal pct = 0;
if (total > 500)
pct = (decimal)0.20;
if (total > 200)
pct = (decimal)0.15;
if (total > 100)
pct = (decimal)0.10;
// Calculate the discount amount
decimal discount = total * pct;
// Subtract a dollar for every item ordered
discount -= (decimal)count;
// Make sure it’??s not less than zero
if (discount < 0)
discount = 0;
Console.WriteLine("Discount computed: ${0}", discount.ToString());
return discount;
}
}
}
ComputeDiscount()方法接收Order类和total两个参数并返回本次预订的折扣金额。
按F6重新生成程序.
使用InvokeMethod活动
接下来你可以使用InvokeMethod活动来执行ComputeDiscount()方法。在流程设计器中的”Check Stock”活动之前拖入InvokeMethod活动,并将DisplayName属性设置为Calculate Discount。
指定目标对象
TargetType属性指定要将要使用回调方法的类,在下拉框中选择【浏览类型…】一个Net类型选择对话框,展开OrderProcess程序集,然后选择OrderDiscount类,如Figure 7-9.图所示:
Figure 7-9. Selecting the OrderDiscount class
提示:
你可以使用两种方法来指定被回调方法的对象。如果方法是在静态类中,你可以像我上面做的哪样简单地指定TargetType属性。
如果类不是静态的则必须指定一个对象的实例,最好的方式是定义一个类类型的变量,然后在TargetObject属性中指定变量名。如果有多个实例,则需要去控制哪个实例被使用,你可以在Assign活动或自定义活动之前设置变量去执行InvokeMethod活动。如果你已经设置了TargetObject属性则不必要去指定TargetType属性值。
MethodName属性中设置ComputeDiscount方法名。
指定参数
将鼠标移到” Calculate Discount”错误指示器的上面。你将会看到如下所示的提示信息。
Figure 7-10. InvokeMethod error message
显示这样的错误是因为还没有定义参数。” Calculate Discount”属性窗口中点击【Parameters】右边的省略号按钮将弹出如下图所示的属性对话框:
Figure 7-11. An empty Parameters collection
点击【创建参数】在Direction(方向)中选择In(输入),Type(类型)中选择Order,Value(值)设置为OrderInfo,OrderInfo是正在处理的订单类Order的变量,使用同样的方法再创建第二个输入参数TotalAmount,Type(类型)设置为Decimal,Value(值)设置为TotalAmount。操作完成后我们将看到如下图Figure 7-12所示的结果:
Figure 7-12. The completed Parameters collection
指定结果
ComputeDiscount()方法返回decimal类型的折扣金额,您需要创建一个变量来存储返回的结果。切换工作流设计器左下角的【变量】点击【创建变量】将Name设置为Discount,变量类型设置为Decimal,并且将范围设置为Sequence。变量定义清单看起来就像下图Figure 7-13所示:
Figure 7-13. Adding a Discount variable
选择InvokeMethod活动,在属性窗口中将Result属性设置为Discount,设置完成后InvokeMethod的属性界面如下图Figure 7-14.所示:
Figure 7-14. The completed Properties window
“Calculate Discount”活动如下图Figure 7-15.所示:
Figure 7-15. The Calculate Discount activity
添加折扣
最后一步从当前订单总额中减去折扣,拖动Assign活动到“Calculate Discount”活动的下方,将Assign活动的To和Value属性分别设置为TotalAmount和TotalAmount – Discount。设置结果如下图Figure 7-16.所示:
Figure 7-16. Partial workflow diagram
运行程序
按F5运行程序,你将会看到如下所示的结果:
Order Received
The item total is: $105
Discount computed: $4.5
Item is out of stock - Item Code: 12346
The total amount is: $160.5
Workflow returned $160.5 for my order total
Press ENTER to exit