• WF3 状态机工作流学习心得


    虽然用了一段工作流了, 但有些概念还是模模糊糊的, 比较晕, 没有真正理解, 现在从头学起.

    <<WF高级程序设计>>一书的重要概念摘抄如下:

    1.应用程序与工作流运行时的关系.

    应用程序承载工作流运行时, 运行时则承载单独的工作流实例.

    2.WF的服务。

    WF中的服务可分为核心服务和本地服务。核心服务由WF定义,而本地服务(也称为“数据交换服务”)则是开发人员自定义的。
    运行时引擎必须在启动前先加载核心服务, 一旦调用了StartRuntime方法,就不允许添加核心服务了.
    本地服务是可选服务, 有时也叫数据交换服务, 它可在工作流实例和宿主应用程序之间的通信中充当管道.
    本地服务通常的用处是:使用本地服务在工作流实例与宿主之间进行通信。比如,数据最初从宿主应用程序的某个运行结果中传入工作流,工作流实例在处理完数据后返还给宿主。
    本地服务是一个类,该类实现本地服务接口(使用 ExternalDataExchangeAttribute 标记的接口)并被添加到 ExternalDataExchangeService。
    ExternalDataExchangeService 服务引发具有从 ExternalDataEventArgs 派生的事件数据的事件时,HandleExternalEventActivity 接收数据。


    3.有关状态机的活动

    3.1 状态管理活动:

    3.1.1 StateActivity表示一个单独的状态,它是所有状态机工作流的主要构建块,它通常直接添加到状态机工作流的顶级视图中,也可以作为子活动添加到其他StateAcitvity中。

    只有一组活动允许直接添加到StateActivity中作为其子活动,这组活动包括EventDrivenActivity,StateInitializationActivity,StateFinalizationActivity和其他StateAcitvity.

    3.1.2 SetStateActivity则用于转换到其他状态, 它决定了顶级状态视图中活动之间的连线.

    它通常定义为EventDrivenActivity的子活动,而且必须是活动序列的最后一个活动。

    3.1.3 StateInitializationActivity,StateFinalizationActivity
    开始和结束活动,不提.

    3.2 事件处理活动: EventDrivenAcitvity用于等待接收外部事件.

    EventDrivenAcitvity位于StateActivity中,是一个复合活动,它包含了自己的一组子活动,它用来等待外部事件,等待的事件会触发那个状态的其他处理过程,进行一些真正的工作而执行完其他活动后,通常还会执行一个SetStateActivity来转换到下一个状态。

    EventDrivenAcitvity包含的第一个子活动必须实现IEventActivity接口,当事件到达时,工作流用此接口来通知活动,实现了该接口的标准活动有HandleExternalEventActivity, DelayActivity...

    3.3 本地服务通讯活动: HandleExternalEventActivity用于接收本地服务中的事件,在工作流和本地服务之间进行通信。

    该活动从不单独使用,它必须包含在EventDrivenAcitvity内,且必须是第一个子活动。EventDrivenAcitvity定义了接收到事件时执行的分支,HandleExternalEventActivity实际上就是接收事件并开始分支执行过程的那个活动。

    该事件必须定义在一个用ExternajlDataExchangeAttribute修饰的接口中,而且还必须实现为本地服务。指定事件处理程序的方法是设置HandleExternalEventActivity的InterfaceType, EventName及e属性,同时,还需要创建一个invoked的工作流方法。任何需要处理的事件都必须使用从ExternalDataEventArgs派生的参数,如果不这样,参数就无法和事件一起传递进来。


    3.4 事务活动: TransactionScopeActivity, 也是一个复合活动, 可以包含其他子活动, 都使用同一个事务.

    需要注意的是, 如果工作流包含了TransactionScopeActivity, 则必须在工作流运行时初始化时注册持久性服务.

    4.依赖属性

    这个非常重要, 是wf, wpf的基石, 一定要搞懂. 我理解着, 它和固定的正常的属性相比, 它代表了临时的, 非固有的, 和宿主程序交互的输入, 输出变量.

    代码如下: 

            public static DependencyProperty LzdProperty = DependencyProperty.Register("Lzd", typeof(string), typeof(Workflow1));

            [Description("Lzd")]
            [Category("Lzd Category")]
            [Browsable(true)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
            public string Lzd
            {
                get
                {
                    return ((string)(base.GetValue(Workflow1.LzdProperty)));
                }
                set
                {
                    base.SetValue(Workflow1.LzdProperty, value);
                }
            }

     它随着工作流实例的创建而一次性的传递进来, 代码如下:   

     Dictionary<string, object> dic = new Dictionary<string, object>();
     dic.Add("Lzd","liuzhendong");

     WorkflowInstance wInstance = workflowRuntime.CreateWorkflow(typeof(Workflow1), dic);

    注意: Dictionary<string, object>词典必须是string, object类型的, 其他都不行, 这是CreateWorkflow构造函数中要求的.


    5.工作流数据持久化

    string connectionString = "...";
    SqlWorkflowPersistenceService m_SqlWorkflowPersistenceService = new SqlWorkflowPersistenceService(connectionString);
    workFlowRuntime.AddService(m_SqlWorkflowPersistenceService);

    构造函数有两个:

    public SqlWorkflowPersistenceService(string connectionString);
    public SqlWorkflowPersistenceService(string connectionString, bool unloadOnIdle, TimeSpan instanceOwnershipDuration, TimeSpan loadingInterval);

    第二个构造函数的几个参数意义如下:

    //   unloadOnIdle:表示工作流变成空闲状态时是否应该进行持久化并从内存中卸载.
    //   instanceOwnershipDuration:表示工作流运行时实例为一个工作流实例保持拥有锁的时间长度.它的主要应用环境是包含多个工作流宿主的服务器场所,并且这些工作流宿主都使用了相同的持久化存储.
    //   loadingInterval:轮询的间隔时间,被重新加载的空闲工作流.(不太明白) 

    6.同步的工作流执行

    执行工作流时如果不加载自己的计划服务,运行时会默认自动加载DefaultWorkflowSchedulerService计划服务类,它会使用一个线程池来异步的执行工作流。

    如果要想同步的执行,可以在运行时添加ManualWorkflowSchedulerService计划服务类,不同之处是这个服务使用了宿主应用程序的线程来执行工作流。

    System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService manualService = new System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService();
    workFlowRuntime.AddService(manualService);
    workFlowRuntime.StartRuntime();      

     //创建工作流实例.
    WorkflowInstance wInstance = workflowRuntime.CreateWorkflow(typeof(Workflow1));

    //启动工作流实例
    wInstance.Start();

    显示启动运行时StartRuntime并不是必须的,这里调用它是为了强调添加计划服务这一动作必须在启动工作流运行时之前进行。而之后创建工作流实例并Start也并不会真正启动工作流的执行过程,而是schedule的RunWorkflow方法。

    ManualWorkflowSchedulerService ss = wr.GetService(typeof(ManualWorkflowSchedulerService)) as ManualWorkflowSchedulerService;
    ss.RunWorkflow(GlobalID)


    7.一个实例的步骤

    7.1创建一个Sequence Workflow Console Application项目.

    7.2创建Account类

    namespace WorkflowConsoleApplication1
    {
        [Serializable]
        public class Account
        {
            public Int32 UserID { get; set; }
            public String UserName { get; set; }
            public Double Balance { get; set; }
        }
    }

    7.3 创建本地服务接口

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Workflow.Activities;

    namespace WorkflowConsoleApplication1
    {
        [ExternalDataExchange]
        public interface IAccountServices
        {
            Account AdjustBalance(Int32 UserID, Double adjustment);
        }
    }

    7.4 创建本地服务

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace WorkflowConsoleApplication1
    {
        public class AccountService : IAccountServices
        {
            private Dictionary<Int32, Account> _account = new Dictionary<int, Account>();

            public AccountService()
            {
                Account account = new Account() { UserID = 101, UserName = "lzd", Balance = 100 };
                _account.Add(account.UserID, account);

                account = new Account() { UserID = 102, UserName = "lcj", Balance = 60 };
                _account.Add(account.UserID, account);

                account = new Account() { UserID = 103, UserName = "ly", Balance = 40 };
                _account.Add(account.UserID, account);
            }

            public Account AdjustBalance(Int32 UserID, Double adjustment)
            {
                Account account = null;
                if (_account.ContainsKey(UserID))
                {
                    account = _account[UserID];
                    account.Balance += adjustment;
                }
                return account;
            }
        }
    }

    7.5 修改启动程序, 传入参数

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Workflow.Runtime;
    using System.Workflow.Runtime.Hosting;

    namespace WorkflowConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
                {
                    AutoResetEvent waitHandle = new AutoResetEvent(false);
                    workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) { waitHandle.Set(); };
                    workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
                    {
                        Console.WriteLine(e.Exception.Message);
                        waitHandle.Set();
                    };

                    Dictionary<string, object> args1 = new Dictionary<string, object>();
                    args1["UserID"] = 101;
                    args1["Balance"] = -20;

                    WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1), args1);
                    instance.Start();

                    waitHandle.WaitOne();
                }
            }
        }
    }

    注意:启动工作流时AutoResetEvent的使用,如果去掉加粗的语句,直接运行,系统会提示异常:Cannot access a disposed object. Object name: 'WorkflowRuntime'.

    AutoResetEvent的用法可以参看下面的文章:

    AutoResetEvent 类
    http://msdn.microsoft.com/zh-cn/library/system.threading.autoresetevent(v=vs.80).aspx
    浅谈AutoResetEvent的用法
    http://hi.baidu.com/iaskall/blog/item/1938e00045f87012738b6526.html
    C#多线程AutoResetEvent与ManualResetEvent
    http://hi.baidu.com/kissxrl/blog/item/fe59499055d6518ba877a450.html

    援引“浅谈AutoResetEvent的用法”一文的总结:要等Set()成功运行后,WaitOne()才能够获得运行机会; Set() 这个方法简单点说就相当于一个开关,如果没有执行set()方法,下面的waitOne()就等不到让它执行的通知,这样一来waitOne后面的语句也不会执行了,waitone()就会傻等下去...一直等到set()执行后才会执行它后面的操作, 也就是说,如果 AutoResetEvent.Set()上面的线程完全被执行,AutoResetEvent.WaitOne()控制下的线程才被执行.

    7.6 创建工作流类,并添加属性参数

    using System;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Collections;
    using System.Linq;
    using System.Workflow.ComponentModel.Compiler;
    using System.Workflow.ComponentModel.Serialization;
    using System.Workflow.ComponentModel;
    using System.Workflow.ComponentModel.Design;
    using System.Workflow.Runtime;
    using System.Workflow.Activities;
    using System.Workflow.Activities.Rules;

    namespace WorkflowConsoleApplication1
    {
        public sealed partial class Workflow1 : SequentialWorkflowActivity
        {
            public Int32 UserID { set; get; }
            public Double Balance { set; get; }
            public Account Account { get; set; }

            public Workflow1()
            {
                InitializeComponent();
            }
        }

    }

    从外部传进来的Dictionary中的属性,终归要和工作流类中的属性自动的逐一严格匹配,否则在工作流类中就无法访问到它们,也就无法使用了。假如去掉加粗的三行,运行系统,则会报错:

    The activity 'Workflow1' has no public writable property named 'UserID'.

    7.7 添加自定义活动CodeActivity.cs,继承自Activity类,并添加依赖属性,并重写Execute方法, 最后将它绑定到工作流中。

    using System;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Collections;
    using System.Linq;
    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 WorkflowConsoleApplication1
    {
        public partial class CodeActivity : Activity
        {
            public static DependencyProperty UserIDProperty = DependencyProperty.Register("UserID", typeof(Int32), typeof(CodeActivity));

            [Description("UserID")]
            [Category("UserID Category")]
            [Browsable(true)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
            public Int32 UserID
            {
                get
                {
                    return ((Int32)(base.GetValue(CodeActivity.UserIDProperty)));
                }
                set
                {
                    base.SetValue(CodeActivity.UserIDProperty, value);
                }
            }

            public static DependencyProperty BalanceProperty = DependencyProperty.Register("Balance", typeof(Double), typeof(CodeActivity));

            [Description("Balance")]
            [Category("Balance Category")]
            [Browsable(true)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
            public Double Balance
            {
                get
                {
                    return ((Double)(base.GetValue(CodeActivity.BalanceProperty)));
                }
                set
                {
                    base.SetValue(CodeActivity.BalanceProperty, value);
                }
            }

            public static DependencyProperty AccountProperty = DependencyProperty.Register("Account", typeof(Account), typeof(CodeActivity));

            [Description("Account")]
            [Category("Account Category")]
            [Browsable(true)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
            public Account Account
            {
                get
                {
                    return ((Account)(base.GetValue(CodeActivity.AccountProperty)));
                }
                set
                {
                    base.SetValue(CodeActivity.AccountProperty, value);
                }
            }

            public CodeActivity()
            {
                InitializeComponent();
            }

            protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
            {
                IAccountServices s = executionContext.GetService<IAccountServices>();
                if (s == null)
                {
                    throw new Exception("there is an exception");
                }
                this.Account = s.AdjustBalance(UserID, Balance);

                return base.Execute(executionContext);
            }
        }
    }

    总结:这里用和工作流类中同名的属性,但不同的是,工作流类中是普通的属性,这里却是依赖属性,为何?因为这里的属性是假的,空的,没有实际的数据,实际的数据还在工作流类的属性中,就一份,节省空间了。

    试着把CodeActivity中的依赖属性改成普通属性,会发生啥?编译倒不会出错,一会儿才能看到恶果。

    然后,编译后打开工作流设计器,将CodeActivity拖到顺序工作流中,进行装配,如果进行到这里,就运行项目,则会得不到本地服务,为何?因为CodeAcitivity还没有和Workflow1工作流类进行属性绑定呢,工作流中的属性不经过一番绑定的手脚是无法传给CodeActivity的。

    那好,现在就开始绑定,在工作流设计器中右键CodeAcitity活动打开属性页,看图一,看到右边有UserID, Balance, Account三个属性,但是,右边没有下拉选项,无法绑定,为何?

    这就是因为把CodeActivity中的属性改为普通属性造成的,因为子活动要绑定要工作流上,是个片段,他本身没数据,要依赖工作流这个大树的数据,所以就像借东西用,要依赖才行。

    把CodeActivity中的属性得改回原来的依赖属性,再看图2,就不一样了,属性前面有个黄色小标志,右边还有浏览选择的按钮,这就对了。

     

    按照图3,图4的方式一路选择

    到现在,就算绑定好了。

    看看designer.cs中的代码

                this.codeActivity1.SetBinding(WorkflowConsoleApplication3.CodeActivity.UserIDProperty, ((System.Workflow.ComponentModel.ActivityBind)(activitybind3)));
                this.codeActivity1.SetBinding(WorkflowConsoleApplication3.CodeActivity.BalanceProperty, ((System.Workflow.ComponentModel.ActivityBind)(activitybind2)));
                this.codeActivity1.SetBinding(WorkflowConsoleApplication3.CodeActivity.AccountProperty, ((System.Workflow.ComponentModel.ActivityBind)(activitybind1)));

    现在能运行了吗? 编译虽然没问题,但实际上还不行, 因为CodeActivity里使用了本地服务, 在启动时要把本地服务添加进去才行.

    7.8  修改启动Main函数

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Workflow.Runtime;
    using System.Workflow.Runtime.Hosting;
    using System.Workflow.Activities;

    namespace WorkflowConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
                {
                    AutoResetEvent waitHandle = new AutoResetEvent(false);
                    workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) { waitHandle.Set(); };
                    workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
                    {
                        Console.WriteLine(e.Exception.Message);
                        waitHandle.Set();
                    };

                    //创建数据交换服务
                    ExternalDataExchangeService dataService = new ExternalDataExchangeService();

                    //将数据交换服务加入运行时
                    workflowRuntime.AddService(dataService);

                    //创建local数据接口服务
                    AccountService localService = new AccountService();

                    //将local数据接口服务加入数据交换服务
                    dataService.AddService(localService);

                    Dictionary<string, object> args1 = new Dictionary<string, object>();
                    args1["UserID"] = 101;
                    args1["Balance"] = -20;

                    WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1), args1);
                    instance.Start();

                    waitHandle.WaitOne();
                }
            }
        }
    }

    现在, 执行就没问题了, debug单句调试时可以看到, 数据已经传入工作流, 本地服务也已启动, 数据也修改了, 一切运行完美.

    但是有一个问题, 如何把工作流的运行结果返回来, 让宿主程序拿到运行结果Account对象?

    7.9 工作流实例运行完毕后取回参数要写在WorkflowCompleted事件的处理方法中, 返回参数在WorkflowCompletedEventArgs中, 查一下该参数的定义:

          public Dictionary<string, object> OutputParameters { get; }

          原来就是一Dictionary.

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

          Account account = (Account)e.OutputParameters["Account"];
          Console.WriteLine(account.UserID + account.UserName + account.Balance);
          waitHandle.Set();
    };

    7.10 疑问: 工作流中到底该用属性还是该用依赖属性? 怎么有时好象都可以?

         测试结果如下:

    workflow codeactivity 绑定结果
    普通属性  普通属性      失败
    依赖属性  普通属性      失败
    依赖属性  依赖属性      成功 
    普通属性  依赖属性      成功 

          表明工作流类用普通,依赖都可以, 而子活动必须要用依赖属性, 也就是大的可以随意, 小的必须依赖大的.

    8.事件驱动活动

    8.1 创建事件参数类

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Workflow.Activities;

    namespace WorkflowConsoleApplication1
    {
        [Serializable]
        public class AccountEventArgs : ExternalDataEventArgs
        {
            public Int32 UserID {set; get;}
            public Double Balance {set; get;}

            public AccountEventArgs(Int32 _UserID, Double _Balance)
            {
                UserID = _UserID;
                Balance = _Balance;
            }

        }
    }

         编译通不过, 报告: 'System.Workflow.Activities.ExternalDataEventArgs' does not contain a constructor that takes '0' arguments.

         必须要在构造函数中添加一个GUID的参数, 还必须是第一位的.

    namespace WorkflowConsoleApplication1
    {
        [Serializable]
        public class AccountEventArgs : ExternalDataEventArgs
        {
            public Int32 UserID {set; get;}
            public Double Balance {set; get;}

            public AccountEventArgs(Guid instanceId, Int32 _UserID, Double _Balance) : base(instanceId)
            {
                UserID = _UserID;
                Balance = _Balance;
            }

        }
    }

    8.2 修改本地服务接口, 在原来代码的基础上增加一个事件.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Workflow.Activities;

    namespace WorkflowConsoleApplication1
    {
        [ExternalDataExchange]
        public interface IAccountServices
        {
            event EventHandler<AccountEventArgs> AdjustBalanceEvent;

            Account AdjustBalance(Int32 UserID, Double adjustment);
        }
    }

    8.3 修改本地服务,实现event并添加激发event的方法FireAdjustBalanceEvent.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace WorkflowConsoleApplication1
    {
        public class AccountService : IAccountServices
        {
            public event EventHandler<AccountEventArgs> AdjustBalanceEvent;

            private Dictionary<Int32, Account> _account = new Dictionary<int, Account>();

            public AccountService()
            {
                Account account = new Account() { UserID = 101, UserName = "lzd", Balance = 100 };
                _account.Add(account.UserID, account);

                account = new Account() { UserID = 102, UserName = "lcj", Balance = 60 };
                _account.Add(account.UserID, account);

                account = new Account() { UserID = 103, UserName = "ly", Balance = 40 };
                _account.Add(account.UserID, account);
            }

            public void FireAdjustBalanceEvent(Guid _InstanceId, Int32 _UserID, Double _Balance)
            {
                if (AdjustBalanceEvent != null)
                {
                    AccountEventArgs aeargs = new AccountEventArgs(_InstanceId, _UserID, _Balance);
                    AdjustBalanceEvent(null, aeargs);
                }          
            }

            public Account AdjustBalance(Int32 _UserID, Double _Balance)
            {
                Account account = null;
                if (_account.ContainsKey(_UserID))
                {
                    account = _account[_UserID];
                    account.Balance += _Balance;
                }
                return account;
            }
           
        }
    }

    8.4 修改工作流,增加一个handleExternalEventActivity1活动, 并给该活动设置本地服务接口InterfaceType, 事件EventName及参数e, 最后写一个Invoked调用方法"DealActionInvoked", 它将用来在AdjustBalanceEvent激发时响应该事件.

        

         DealActionInvoked里可以写代码,也可以什么都不写。

         如果刚才没有绑定e,那就必须在这里, 设置传进来的参数, 如果不设置传进来的参数, 后续的CodeActivity活动就接不到数据了, 实际上handleExternalEventActivity1在这起了一个停止工作流, 等事件激发, 设置参数的作用.

    private void DealActionInvoked(object sender, ExternalDataEventArgs e)
    {
          AccountEventArgs args = e as AccountEventArgs;

          this.UserID = args.UserID;
          this.Balance = args.Balance;
    }

          

    8.5 修改启动程序

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Workflow.Runtime;
    using System.Workflow.Runtime.Hosting;
    using System.Workflow.Activities;

    namespace WorkflowConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
                {
                    AutoResetEvent waitHandle = new AutoResetEvent(false);
                    workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e)
                    {
                        Account account = (Account)e.OutputParameters["Account"];
                        Console.WriteLine(account.UserID + account.UserName + account.Balance);
                        waitHandle.Set();
                    };
                    workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
                    {
                        Console.WriteLine(e.Exception.Message);
                        waitHandle.Set();
                    };

                    //创建数据交换服务
                    ExternalDataExchangeService dataService = new ExternalDataExchangeService();

                    //将数据交换服务加入运行时
                    workflowRuntime.AddService(dataService);

                    //创建local数据接口服务
                    AccountService localService = new AccountService();

                    //将local数据接口服务加入数据交换服务
                    dataService.AddService(localService);

                    WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1));                
                    instance.Start();                              

                    localService.OnAdjustBalance(instance.InstanceId, 101, -20);

                    waitHandle.WaitOne();

                    Console.ReadKey();
                }
            }
        }
    }

    这回那个字典不随着创建工作流一起添加进去了, 用单独调用localService.OnAdjustBalance(instance.InstanceId, 101, -20) 的方式传进去, 这个方法会激发引爆工作流里的事件, 从而执行修改Account的目的.


    8.6 这样, 就完成了和上一个项目同样的功能, 对比7,8 两个项目可以发现,使用事件驱动活动,可以让工作流启动后先停在那里,然后又选择的激发工作流中的事件,从而执行不同的动作,这比工作流一起动就顺序执行动作要灵活的多。

    9.状态机工作流

    9.1 先从一个状态机工作流的例子开始, 比如说要处理订单状态, 提交, 审批通过或拒绝, 结束.

    先把工作流鸟瞰截图share一下, 简简单单, 总共三个状态, 每个步骤里一个handleExternalEventActivity, 一个自定义的CodeAcitvity和一个SetStateActivity.


    9.2 开始从头描述代码

         要处理的订单类代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace WorkflowConsoleApplication2
    {

        [Serializable]
        public class SalesOrder
        {
            public int SalesOrderID { set; get; }
            public string CustomerName { set; get; }
            public string ProductName { set; get; }
            public int Amount { set; get; }
            public decimal UnitPrice { set; get; }
            public decimal TotalMoney { set; get; }
            public int Status { set; get; }
        }
    }

      服务接口:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Workflow.Activities;

    namespace WorkflowConsoleApplication2
    {
        [ExternalDataExchange]
        public interface ISalesOrderService
        {
            event EventHandler<SalesOrderArgs> SubmitEvent;
            event EventHandler<SalesOrderArgs> ApprovalEvent;
            event EventHandler<SalesOrderArgs> RejectEvent;
        }
    }

       本地服务代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace WorkflowConsoleApplication2
    {
        public class SalesOrderService: ISalesOrderService
        {
            public event EventHandler<SalesOrderArgs> SubmitEvent;
            public event EventHandler<SalesOrderArgs> ApprovalEvent;
            public event EventHandler<SalesOrderArgs> RejectEvent;

            public void FireSubmitEvent(Guid instanceID, SalesOrder salesOrder)
            {
                if (SubmitEvent != null)
                {
                    SalesOrderArgs args = new SalesOrderArgs(instanceID, salesOrder);
                    SubmitEvent(null, args);
                }
            }

            public void FireApprovalEvent(Guid instanceID, SalesOrder salesOrder)
            {
                if (ApprovalEvent != null)
                {
                    SalesOrderArgs args = new SalesOrderArgs(instanceID, salesOrder);
                    ApprovalEvent(null, args);
                }
            }

            public void FireRejectEvent(Guid instanceID, SalesOrder salesOrder)
            {
                if (RejectEvent != null)
                {
                    SalesOrderArgs args = new SalesOrderArgs(instanceID, salesOrder);
                    RejectEvent(null, args);
                }
            }
        }
    }

      事件参数类:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Workflow.Activities;

    namespace WorkflowConsoleApplication2
    {
        [Serializable]
        public class SalesOrderArgs : ExternalDataEventArgs
        {
            public SalesOrder SalesOrder { set; get; }

            public SalesOrderArgs(Guid instanceID, SalesOrder salesOrder) : base(instanceID)
            {
                this.SalesOrder = salesOrder;
            }
        }
    }

      工作流类: 

    using System;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Collections;
    using System.Linq;
    using System.Workflow.ComponentModel.Compiler;
    using System.Workflow.ComponentModel.Serialization;
    using System.Workflow.ComponentModel;
    using System.Workflow.ComponentModel.Design;
    using System.Workflow.Runtime;
    using System.Workflow.Activities;
    using System.Workflow.Activities.Rules;

    namespace WorkflowConsoleApplication2
    {
        public sealed partial class Workflow1 : StateMachineWorkflowActivity
        {
            public static DependencyProperty SalesOrderArgsProperty = DependencyProperty.Register("SalesOrderArgs", typeof(SalesOrderArgs), typeof(Workflow1));

            [Description("SalesOrderArgs")]
            [Category("SalesOrderArgs Category")]
            [Browsable(true)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
            public SalesOrderArgs SalesOrderArgs
            {
                get
                {
                    return ((SalesOrderArgs)(base.GetValue(Workflow1.SalesOrderArgsProperty)));
                }
                set
                {
                    base.SetValue(Workflow1.SalesOrderArgsProperty, value);
                }
            }

            public Workflow1()
            {
                InitializeComponent();
            }

            private void HandleSumitInvoked(object sender, ExternalDataEventArgs e)
            {
                Console.WriteLine("HandleSumitInvoked");
            }

            private void HandleApprovalInvoked(object sender, ExternalDataEventArgs e)
            {
                Console.WriteLine("HandleApprovalInvoked");
            }

            private void HandleRejectInvoked(object sender, ExternalDataEventArgs e)
            {
                Console.WriteLine("HandleRejectInvoked");
            }

        }

    }

      自定义CodeActivity类,其它两类一样,就不贴出来了:

    using System;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Collections;
    using System.Linq;
    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 WorkflowConsoleApplication2
    {
        public partial class SubmitActivity : Activity
        {
            public static DependencyProperty SalesOrderArgsProperty = DependencyProperty.Register("SalesOrderArgs", typeof(SalesOrderArgs), typeof(SubmitActivity));

            [Description("SalesOrderArgs")]
            [Category("SalesOrderArgs Category")]
            [Browsable(true)]
            [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
            public SalesOrderArgs SalesOrderArgs
            {
                get
                {
                    return ((SalesOrderArgs)(base.GetValue(SubmitActivity.SalesOrderArgsProperty)));
                }
                set
                {
                    base.SetValue(SubmitActivity.SalesOrderArgsProperty, value);
                }
            }

            public SubmitActivity()
            {
                InitializeComponent();
            }

            protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
            {
                this.SalesOrderArgs.SalesOrder.Status = 0;
                return base.Execute(executionContext);
            }
        }
    }

      启动Main函数:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Workflow.Runtime;
    using System.Workflow.Runtime.Hosting;
    using System.Workflow.Activities;

    namespace WorkflowConsoleApplication2
    {
        class Program
        {
            static void Main(string[] args)
            {
                using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())
                {
                    AutoResetEvent waitHandle = new AutoResetEvent(false);
                    workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) {
                        SalesOrderArgs soArgs = (SalesOrderArgs)e.OutputParameters["SalesOrderArgs"];
                        Console.WriteLine(soArgs.SalesOrder.SalesOrderID + soArgs.SalesOrder.CustomerName + soArgs.SalesOrder.ProductName + " status" + soArgs.SalesOrder.Status);
                        waitHandle.Set();
                    };
                    workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
                    {
                        Console.WriteLine(e.Exception.Message);
                        waitHandle.Set();
                    };

                    //创建数据交换服务
                    ExternalDataExchangeService dataService = new ExternalDataExchangeService();

                    //将数据交换服务加入运行时
                    workflowRuntime.AddService(dataService);

                    //创建local数据接口服务
                    SalesOrderService localService = new SalesOrderService();

                    //将local数据接口服务加入数据交换服务
                    dataService.AddService(localService);

                    WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication2.Workflow1));

                    instance.Start();                          

                    SalesOrder so = new SalesOrder() { SalesOrderID = 100, CustomerName = "上海汽车股份有限公司", ProductName = "ACM", UnitPrice = 1200, Amount = 10, TotalMoney = 12000, Status = -1 };

                    localService.FireSubmitEvent(instance.InstanceId, so);    
                   

                    waitHandle.WaitOne();
                }
            }
        }
    }

    9.3 运行,ok了。


    推荐资料:

    1.在宿主中使用参数与实例通信
    http://www.cnblogs.com/foundation/archive/2006/09/26/514869.html

    2.<<WF高级程序设计>>

  • 相关阅读:
    关于hexo-abbrlink链接undefined
    如何修改layer-layui中的confirm
    cmder的segmentation fault错误修复
    论好的代码习惯的养成/做一个优雅的coder
    50行代码写的一个插件,破解一个H5小游戏
    慎用array_filter函数
    python:if 语句的使用方法
    python:for语句的使用方法
    关于python3 发送邮件
    zookpeer的安装与配置
  • 原文地址:https://www.cnblogs.com/liuzhendong/p/2277486.html
Copyright © 2020-2023  润新知