在这一章,你将添加逻辑去验证订单项是否有存货。为了去完成它,你需要重复用同样的方式验证每一个订单项,如果有一项没有存货,你将抛出一个异常,这个异常可以被你的工作流捕获。
重新利用Chapter5项目
打开Visual Studio 2010 创建一个新的项目,选择Blank Solution模板,如图Figure6-1所示。输入解决方案名为Chapter06。
复制Chapter05目录下的OrderProcess文件夹到Chapter06的目录下。在Solution Explorer中,右击Chapter06解决方案,选择Add>Existing Project。Add Existing Project对话框会出现。选择Chapter06\OrderProcess下的OrderProcess.csproj文件。(这跟上一章的做法是一样的。)
添加检验存货活动
现在你将要添加逻辑去验证是否有充足的存货给订单了。
TryCatch活动
打开OrderWF.xaml文件的设计视图。拖拉一个TryCatch活动到“Handling Charges”活动的下面。修改DisplayName为Check Stock,点击活动右上角的展开按钮。设计视图如图Figure6-2所示。
TryCatch活动有三个节点。在Try节点中,你可以放置一系列可能产生异常的活动。在Catches节点中,你可以定义一个或多个Catch对象。每一Catch对象处理一个特定的异常。Finally节点是可选的。你可以放置一系列活动到这里,这里的活动是在Try活动(和任何被异常引发的Catch对象)执行完之后执行的。
定义一个异常
你将定义一个异常,这个异常是当没有存货时抛出的。打开Order.cs文件,添加下面的代码去定义OutOfStockException类。
//----------------------------------------
//define the exception to be thrown if an item
//is out of stock
//----------------------------------------
public class OutOfStockException : Exception
{
public OutOfStockException()
: base()
{ }
public OutOfStockException(string message)
: base(message)
{ }
}
这应该添加在Order类定义之后,在OrderProcess命名空间中。完整的Order.cs如下所示:
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; }
}
//----------------------------------------
//define the exception to be thrown if an item
//is out of stock
//----------------------------------------
public class OutOfStockException : Exception
{
public OutOfStockException()
: base()
{ }
public OutOfStockException(string message)
: base(message)
{ }
}
}
ForEach活动
拖放一个ForEach活动到Try节点中,设置DisplayName为Check Each Item。点击展开按钮,在Expression中输入以下代码:
OrderInfo.Items
这定义了循环访问订单项中的每一项。(ForEach活动的详细信息请参考第五章。)在属性窗口中,对于TypeArgument属性,选在Browse for Types,然后选择OrderProcess引用下的OrderItem类。(跟第五章一样)。
If活动
拖拉一个If活动到Body节点,展开活动,设置DisplayName为If Out of Stock。如图Figure6-3所示。
在Condition属性中输入下面代码:
Item.ItemCode = “12346”
提示:在真正的程序开发中,你会从数据库中取出数据,然后验证这些数据是否没有存货。在这个示例中,判断是否没有存货是硬编码的。
Throw活动
拖放一个Throw活动到Then节点。在属性窗口,它只有两个属性:DisplayName和Exception。对于Exception属性,输入下面代码:
New OrderProcess.OutOfStockException(”Item Code: ” + item.ItemCode)
这创建了一个OutOfStockException对象,指定了是哪个订单项是没有存货的信息。“If Out of Stock”活动应该如图Figure6-4所示。
“Check Stock”活动应该如图Figure6-5所示。
点击Add new catch链接。将会出现一个下拉列表,选择Browse for Types,选择OrderProcess引用下的OutOfStockException,如图Figure6-6所示。
图形应该如图Figure6-7所示。
你将会捕获循环的异常,你应该定义可以执行处理这个异常的活动。对于这个项目,你将输出一行信息去指出是哪一项是没有存货的。拖放一个WriteLine活动到Catches节点中,Text属性为:
“Item is out of stock - ” + exception.Message
这行代码从异常中获取信息,信息包含了没有存货的订单项号。收缩“Check Stock”活动然后展开它。“Check Stock”的最终如图Figure6-8所示。
点击F5,结果如下所示:
选择在哪里摆放异常处理时非常重要的。例如你可以把整个工作流摆放到一个TryCatch活动的Try节点中,虽然这是一个简单的方式去捕获所有的异常。但是,这可能没有让你真正的去处理这些异常。让我解析一下。
当一个异常被抛出,如果他不是被父活动捕获的,父活动将会流产,而剩下的子活动将会不会被执行。在这个项目中,例如,异常时被If活动的Then分支抛出的。TryCatch在ForEach活动之外。因为异常没有更早的捕获,当异常到达ForEach活动时,ForEach活动将会停止循环。这意味着一旦异常抛出,工作流将会停止检验其他的订单项。这就是我想说的。
下面的伪代码展示了它是怎么工作的:
Sequence
{
CheckStock (Try)
{
Check Each Item (ForEach)
{
If Out of Stock (If)
{
Throw Exception (Then)
}
}
}
Catch
{
}
Remaining Activities
}
当一个异常抛出,程序会直接去到Catch序列,然后工作流会在Catch这里继续执行下去。这种设计,你可以用来处理费用异常,然而,如果你想继续检验剩下的订单项,你需要把TryCatch活动放到If活动的外面,这会在异常到达ForEach活动之前被处理掉。如下伪代码所示:
Sequence
{
Check Each Item (ForEach)
{
(Try)
{
If Out of Stock (If)
{
Throw Exception (Then)
}
}
Catch
{
}
}
}
因为Catch序列在ForEach活动之内,一旦Catch被执行,工作流会接着去处理下一个订单项,如果,你想在异常发生的时候,整个工程要停止,你可以把TryCatch活动放到整个工作流的最顶层,如下所示:
(Try)
{
Sequence
{
Check Each Item
{
If Out of Stock
{
Throw Exception
}
}
Remaining Activities
}
}
Catch
{
}
因为Catch序列在最外面,在Catch执行完之后,工作流也会完成。