上面介绍了采用CodeActivity来定义工作流。而我们现在来看看另一种向工作流中添加代码的方法——使用自定义活动。我们还是使用前面用到的下单的例子,看看两者有何不同。
采用自定义活动的好处就在于我们开发的代码段是离散、可分离、可复用的组件。一旦测试成功,我们就可以采用拖放的方法将其添加到任意的工作流中。而CodeActivity的复用仅仅限于在当前的工作流中。
定义Custom Activities
OK,下面开始动手。建立一个名为OrderEntryActivities的Sequential Workflow Console Application工程,然后从Project菜单中选择Add Activity,添加名为ValidateAccountActivity的Activity。一般来说,命名的时候以Activity来结尾,比较规范。
进入ValidateAccountActivity.cs的代码,把它的基类由SequenceActivity改为Activity,因为这是一个单一的活动,没有必要采用Sequence。
每个自定义的活动都应该设计为独立的组件,它们有自己的输入和输出参数。一个活动并不知道工作流中其它活动的任何信息,只能通过属性值来进行通讯。为了支持绑定,我们采用从属属性(dependency properties)代替常规的.NET属性。此活动所需的从属属性有下面几个:
l AccountId:int型,标识待验证的帐号;
l IsAccountVerified:bool型,指示AccountId是否有效;
l AvailableCredit:decimal型,包含有帐户的信用卡余额
下面的代码就是添加了从属属性之后的ValidateAccountActivity.cs文件。记着在这里可以使用CodeSnippet来快速插入代码。
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
namespace OrderEntryActivities
{
public partial class ValidateAccountActivity: Activity
{
public ValidateAccountActivity()
{
InitializeComponent();
}
#region Public workflow properties
public static DependencyProperty AccountIdProperty = DependencyProperty.Register("AccountId", typeof(int), typeof(ValidateAccountActivity));
[DescriptionAttribute("AccountId")]
[CategoryAttribute("AccountId Category")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
public int AccountId
{
get
{
return ((int)(base.GetValue(ValidateAccountActivity.AccountIdProperty)));
}
set
{
base.SetValue(ValidateAccountActivity.AccountIdProperty, value);
}
}
public static DependencyProperty AvailableCreditProperty = DependencyProperty.Register("AvailableCredit", typeof(int), typeof(ValidateAccountActivity));
[DescriptionAttribute("AvailableCredit")]
[CategoryAttribute("AvailableCredit Category")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
public int AvailableCredit
{
get
{
return ((int)(base.GetValue(ValidateAccountActivity.AvailableCreditProperty)));
}
set
{
base.SetValue(ValidateAccountActivity.AvailableCreditProperty, value);
}
}
public static DependencyProperty IsAccountVerifiedProperty = DependencyProperty.Register("IsAccountVerified", typeof(bool), typeof(ValidateAccountActivity));
[DescriptionAttribute("IsAccountVerified")]
[CategoryAttribute("IsAccountVerified Category")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
public bool IsAccountVerified
{
get
{
return ((bool)(base.GetValue(ValidateAccountActivity.IsAccountVerifiedProperty)));
}
set
{
base.SetValue(ValidateAccountActivity.IsAccountVerifiedProperty, value);
}
}
#endregion
}
}
属性添加完毕,我们需要实现活动的逻辑。实际执行的逻辑是存放在Execute方法中的。这个方法是由基类提供的虚方法,在我们这个派生类中必须要重载实现。当活动被执行时,这个方法就会被工作流运行时同步调用。模拟查找和验证用户的代码如下所示:
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
switch (AccountId)
{
case 1001:
IsAccountVerified = true;
AvailableCredit = 100.00M;
break;
case 2002:
IsAccountVerified = true;
AvailableCredit = 500.00M;
break;
default:
IsAccountVerified = false;
AvailableCredit = 0;
break;
}
return base.Execute(executionContext);
}
同样的道理,我们继续添加商品验证活动ValidateProductActivity以及下单活动EnterOrderActivity,具体实现参见源码。
现在进入我们最终承载功能的工作流Workflow1.cs代码,首先还是添加从属属性,AccountId和SalesItemId,参见源码。在这个文件中,我们上一种方法里需要添加的私有字段就不复存在了。代码完成之后,请按下F6编译整个工程文件。
切换到设计模式,可以看到在Toolbox中已经有我们自定义的活动了(如果没有,请编译一下工程),如下图所示:
绘制工作流
拖放一个ValidateAccountActivity的实例到工作流中。我们现在需要把该活动的AccountId属性与工作流的对应属性绑定起来。要达到这个目的,切换到该活动的属性窗口,如图:
现在绑定AccountId属性,点击它后面的省略号,打开一个如下所示的绑定对话框。
在这个对话框中,我们可以把工作流或者是别的活动的属性绑定到此活动的属性上。只有从属属性才能实现这种功能。在这个例子中,我们将此活动的AccountId属性绑定到工作流的AccountId上。在运行时,通过Dictionary传递给工作流的属性值也会自动地传递到这个活动来。在这个列表里选中AccountId,点击确定。属性窗口中的另外两个属性,AvailableCredit和IsAccountVerified是活动的输出,现在暂时不需要设置绑定。
现在拖放一个IfEElseActivity到刚才那个活动的下方。这个分支活动将根据validateAccountActivity1的IsAccountVerified属性来确定工作流的走向。左边的分支代表着帐户有效,右侧代表无效。分别将它们重新命名为ifAccountVerified和ifAccountInvalid。
在属性窗口里,设置一个this.validateAccountActivity1.IsAccountVerified==true的Declarative Rule Condition,具体做法就不赘述了。
补完右侧的分支,拖放一个CodeActivity,添加如下代码:
private void codeBadAccountId_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("AccountId {0} 无效", AccountId);
}
同样的方法,我们继续往下去补充验证商品以及下单的工作流逻辑。请参阅我提供的源码示例。就在今天,.NET Framework 3.5发布了正式版,经过我的测试,卸载beta2版的Framework 3.5,再安装正式版的之后,VS2008 beta2仍然可以正常运行。现在的示例统统都是在正式版的Framework 3.5下编译测试通过的。
最后完成之后的工作流看起来应该是下面的样子:
然后就是修改Program.cs文件,调用这个工作流了。大部分都可以从上一个示例里照抄,唯一需要改动的就是下面的加粗部分:
public void RunWorkflow(Dictionary<string, object> wfArguments)
{
// 创建工作流实例并启动
WorkflowInstance instance = _workflowRuntime.CreateWorkflow(typeof(OrderEntryActivities.Workflow1), wfArguments);
instance.Start();
// 等待工作流完成
_waitHandle.WaitOne();
}
好,F5编译执行,如果一切正常的话,结果应与上一节里看到的完全相同。