昨夜飘风
昨 夜, 风, 飘 过; 枯 树, 叶, 飞 落。

源代码下载:OrderEntryCode.zip
    这一部分中,我们来看一看如何向工作流添加自定义逻辑。自定义逻辑在这里指的是向工作流添加我们自己的代码。完成这一目标,可以有两种机制:使用
CodeActivity和开发自定义活动。在后面我们会看到这两种方法是如何实现的。这里用到的示例,是一个订货的流程。我们假定它包含了下述的几个步骤:

验证发送订单的帐号ID,并且查询它的信用卡余额;

验证待购商品的ID,并获取它的价格;

如果帐号和商品都是有效的,并且帐户余额足以支付商品的价格,那么就执行这份订单。

使用CodeActivity

首先建立一个Sequential Workflow Console Application项目,命名为OrderEntryCode

为了处理一个订单,此工作流需要两个参数:AccountIdSalesItemId,分别标识下单者和待售商品。这些参数可以有两种方法在工作流中定义,即通常的.NET类属性(前面已经使用过),或者一个从属属性(dependency property)。从属属性的属性值集中存放在一起,而不是作为一个普通的成员变量定义在类中。这样做的好处就在于允许在运行时将属性值绑定到实例。

为了支持从属属性,这个类必须派生自DependencyObject类。所幸的是我们的System.Workflow.ComponentModel.Activity根类就是派生自它,因此所有的工作流和活动类都支持从属属性。

实际操作一下,我们定义AccountId属性作为此工作流的从属属性,添加下面的代码到Workflow1.cs文件中:

    public static DependencyProperty AccountIdProperty = System.Workflow.ComponentModel.DependencyProperty.Register("AccountId", typeof(int), typeof(Workflow1));

    ///<summary>

    ///标识一个帐户

    ///</summary>

    [Description("标识帐户")]

    [Category("CodeActivity 示例")]

    [Browsable(true)]

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]

    public int AccountId

    {

        get

        {

            return ((int)(base.GetValue(Workflow1.AccountIdProperty)));

        }

        set

        {

            base.SetValue(Workflow1.AccountIdProperty, value);

        }

    }

可以看到,getset通过调用GetValueSetValue方法来获取和更新属性值。这些属性值都被存放在由静态的DependencyProperty域作为键值的库中。这个库很像一个Dictionary对象,基于一个唯一的键值来保存每一个属性。

Visual Studio还提供了专门的snippet来插入从属属性的代码。在你想要插入从属属性的地方右击编辑区,选择Insert SnippetàWorkflowàDependencyProperty – Property,然后一个模板似的代码段就为你准备好了。So good~~~



随后向工作流添加一些私有字段,参见示例源码。

切换到工作流设计模式,拖放一个CodeActivity,这个代码活动负责验证作为输入参数传递进来的AccountId属性。如果AccountId幼小,那么isAccountVerified变量设置为true,并且availableCredit变量设置为信用卡的余额。

使用属性窗口,将CodeActivity的名称改为codeLookupAccount。双击它,填入下面的代码:

    private void codeLookupAccount_ExecuteCode(object sender, EventArgs e)

    {

        // 模拟查询账户

        switch (AccountId)

        {

            case 1001:

                isAccountVerified = true;

                availableCredit = 100.00M;

                break;

            case 2002:

                isAccountVerified = true;

                availableCredit = 500.00M;

                break;

            default:

                isAccountVerified = false;

                availableCredit = 0;

                break;

        }

    }

实际应用中,这里应该是进行数据库查询。这个例子中我们就从简了。切换回设计界面,在CodeActivity的下面再添加一个IfElseActivity,它可以根据isAccountVerified变量的值确定AccountId是否有效。左边的分支代表帐户有效的处理,右边代表无效。将左边的分支改名为ifAccountVerified,把它的Condition属性改为Declarative Rule Condition之后展开它,设置ConditionNamecheckIsAccountVerified(注意,工作流里的所有规则条件都必须拥有一个独立的名称)。点击Expression属性旁的省略号,输入下面的条件:

this.isAccountVerified == true

点击确定,这时候的属性窗口看起来应该是下面的样子:



在继续补充左侧分支之前,先完善右边的分支。将其改名为ifAccountInvalid,拖放一个CodeActivity进去,改名为codeBadAccountId,然后双击添加代码:

    private void codeBadAccountId_ExecuteCode(object sender, EventArgs e)

    {

        Console.WriteLine("AccountId {0} 无效", AccountId);

    }

继续来看左侧的分支,拖放另一个CodeActivity上去,这段代码的目的就是验证待购商品的ID,并且获取其价格。如果SalesItemId属性有效,那么isSalesItemVerified就设置为true,并且salesItemAmount设置为商品的价格。将CodeActivity的名称改为codeLookupItem,双击添加代码:

    private void codeLookupItem_ExecuteCode(object sender, EventArgs e)

    {

        // 模拟商品查询

        switch (SalesItemId)

        {

            case 501:

                isSalesItemVerified = true;

                salesItemAmount = 59.95M;

                break;

            case 502:

                isSalesItemVerified = true;

                salesItemAmount = 199.99M;

                break;

            default:

                isSalesItemVerified = false;

                salesItemAmount = 0;

                break;

        }

    }

下面我们还要对购买者的信用卡余额进行检查。拖放IfElseActivitycodeLookupItem活动下面,将它内部的分支分别改名为ifCreditAvailableifItemProblems。对左侧分支添加Declarative Rule Condition,以checkAvailableCredit为名,输入如下的Expression

this.isSalesItemVerified == True && this.salesItemAmount <= this.availableCredit

在上面放置一个CodeActivity,命名为codeEnterOrder,添加代码:

    private void codeEnterOrder_ExecuteCode(object sender, EventArgs e)

    {

        // 模拟下单

        Console.WriteLine("{0} 的订单已下,商品 ID 为 {1},价格 {2}", AccountId, SalesItemId, salesItemAmount);

    }

向右侧的分支也添加一个CodeActivity,代码如下:

    private void codeInsufficientCredit_ExecuteCode(object sender, EventArgs e)

    {

        Console.WriteLine("商品 {0} 无效或者 AccountId {1} 的信用额度不足 {2}", SalesItemId, AccountId, availableCredit);

    }

最终的工作流看起来应该是下面的样子。



完整代码参见示例。下面我们在Program.csMain方法中加入测试代码,执行这个工作流。修订后的Program.cs代码如下,所有的解释和说明以注释形式添加在代码中。

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;

using System.Workflow.Runtime;

using System.Workflow.Runtime.Hosting;

namespace OrderEntryCode

{

    ///<summary>

    ///执行带有CodeActivity 的 OrderEntry 工作流

    ///</summary>

    class Program : IDisposable

    {

        private WorkflowRuntime _workflowRuntime;

        private AutoResetEvent _waitHandle = new AutoResetEvent(false);

        public Program()

        {

            InitializeWorkflowRuntime();

        }

        #region IDisposable Members

        public void Dispose()

        {

            _workflowRuntime.StopRuntime();

            _workflowRuntime.Dispose();

        }

        /// Program 类实现了IDisposable接口,这并不是一个严格的要求,但为了在Main()方法中使用

        /// using 语法,还是这么做了。在Dispose方法中,StopRuntime和Dispose是从WorkflowRuntime

        ///中调用的。这就确保了工作流运行时引擎被释放,并且允许垃圾回收

        #endregion   

       

        ///<summary>

        ///启动工作流运行时

        ///</summary>

        private void InitializeWorkflowRuntime()

        {

            _workflowRuntime = new WorkflowRuntime();

            _workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e)

            {

                _waitHandle.Set();

            };

            _workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)

            {

                Console.WriteLine(e.Exception.Message);

                _waitHandle.Set();

            };

        }

        ///<summary>

        ///运行工作流

        ///</summary>

        ///<param name="wfArguments"></param>

        public void RunWorkFlow(Dictionary<string, object> wfArguments)

        {

            // 创建一个工作流实例并启动它

            WorkflowInstance instance = _workflowRuntime.CreateWorkflow(typeof(OrderEntryCode.Workflow1), wfArguments);

            instance.Start();

            // 等待工作流完成

            _waitHandle.WaitOne();

        }

        static void Main(string[] args)

        {

            using (Program instance=new Program())

            {

                // 创建一个带有输入参数的字典

                Dictionary<string, object> wfArguments = new Dictionary<string, object>();

                wfArguments.Add("AccountId", 1001);

                wfArguments.Add("SalesItemId", 501);

                // 运行工作流

                instance.RunWorkFlow(wfArguments);

                // 修改参数为另一个帐户和商品,再次运行工作流

                wfArguments.Clear();

                wfArguments.Add("AccountId", 2002);

                wfArguments.Add("SalesItemId", 502);

                instance.RunWorkFlow(wfArguments);

                // 这一次帐户的余额不足

                wfArguments.Clear();

                wfArguments.Add("AccountId", 1001);

                wfArguments.Add("SalesItemId", 502);

                instance.RunWorkFlow(wfArguments);

                // 使用一个无效的帐户运行工作流

                wfArguments.Clear();

                wfArguments.Add("AccountId", 9999);

                wfArguments.Add("SalesItemId", 501);

                instance.RunWorkFlow(wfArguments);

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

                Console.ReadLine();

            }

        }

    }

}

F5执行,看看是不是我们预期的结果J

posted on 2008-02-26 15:10  昨夜飘风  阅读(2187)  评论(3编辑  收藏  举报