zoukankan      html  css  js  c++  java
  • WWF系列之关于Host与WorkflowInstance之间的通讯

    关于HostWorkflowInstance之间的通讯

    工作流是将程序(实际上就是业务)流程独立出来,建立健壮的可伸缩的交互式的流程管理。其基本任务是保证每一个步骤必须严格地执行且仅可以执行一遍。

    WF和其宿主(Host)之间必须能够进行通讯,不然就失去了“交互性”,这里介绍三种方法。

     

    1.   使用参数

    如在workflow1中有定义两个属性:

    private string inputParam;

    private bool outputParam;

    public string InputParam

    {

         set { inputParam = value; }

    }

    public bool OutputParam

    {

         get { return outputParam; }

    }

     

    如何从Host传数据到WF

    使用Dictionary<string, object>对象可以传参数给WorkflowRuntimeCreateWorkflow方法,这种方法可以在WF初始化的时候,给其公开的属性赋值。

    我们来看如何给workflow1.InputParam属性赋值:

     

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

    myParams.Add("InputParam", "tuyile006.cnblogs.com");

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

    instance.Start();

     

    需要注意的是Dictionary集合中的Key名称必须要跟WF中公共属性的名称一致。

     

     

    如何从WF中传数据到Host

    WorkflowRuntimeWorkflowCompleted事件里面有一个WorkflowCompletedEventArgs参数,可以通过这个参数的OutputParameters属性获取WF中的公共属性的值。

    我们来看如何获得workflow1.OutputParam的值:

     

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

                        if ((bool)(e.OutputParameters["OutputParam"]))

                        {

                            Console.WriteLine("OutputParam=true;");

                        }

                        else

                        {

                            Console.WriteLine("OutputParam=false;");

                        }

                        waitHandle.Set();

                    };

     

    同前面的一样,OutputParameters集合中的键值名称必须是WF的公共属性名称。

     

    2.   定义通信服务接口和类

    这种方法是比较重要的,因为前面所讲传参数的方法只能在WF初始化的时候通信,而现在要介绍的方法主要用于与WF实时交互,也就是在WF运行过程中进行通信。

    先建一个类库项目,添加对

    System.Workflow.Runtime;

    System.Workflow.Activities;

    System.Workflow.ComponentModel;

    的引用,如图:

     

     

    修改Class1.cs的代码为:

    using System;

    using System.Collections.Generic;

    using System.Workflow.Runtime;

    using System.Workflow.Activities;

    using System.Workflow.ComponentModel;

    using System.Text;

     

    namespace ClassLibrary1

    {

        [ExternalDataExchange]

        public interface ICommService

        {

            //用于从WF中触发Host中的方法

            void CallTheHost(string param);

     

            //用于从Host中触发WF中的事件,这里的ExternalDataEventArgs可以自定义

            event EventHandler<ExternalDataEventArgs> NotigyTheWorkflow;

        }

    }

     

    其中的CallTheHost方法契约用于从WF中调用Host的方法,而事件则正好相反。接口必须使用ExternalDataExchange属性声明,这样WF架构才会找到本接口并生成Activity控件。

    现在我们来增强一下实用性,派生ExternalDataEventArgs类来自定义自己的数据,新建如下类:

    using System;

    using System.Collections.Generic;

    using System.Workflow.Runtime;

    using System.Workflow.Activities;

    using System.Workflow.ComponentModel;

    using System.Text;

     

    namespace ClassLibrary1

    {

        [Serializable]

        public class MyEventArgs : ExternalDataEventArgs

        {

            private string msg;

            public string Msg

            {

                get

                {

                    return msg;

                }

            }

            public MyEventArgs(Guid instanceid, string pmsg)

                : base(instanceid)

            {

                msg = pmsg;

            }

        }

    }

     

    建好之后IcommService接口中的NotigyTheWorkflow事件的参数可以改成MyEventArgs

    event EventHandler<MyEventArgs> NotigyTheWorkflow;

    这里需要注意两点:MyEventArgs必须有Serializable属性声明,因为工作流与宿主的通讯数据是使用串行化数据进行的;另外就是MyEventArgs构造函数中的instanceid参数,这个也是必须的,因为ExternalDataEventArgs的构造函数中当前工作流实例的ID是必须的参数。

     

    下面介绍如何使用本通讯接口。

    将刚刚做好的类库编译成ClassLibrary1.dll文件;

    打开VS2008命令提示窗口,输入如下指令:

    Wca.exe  /l:cs   /o:c:\    /n:MyComm    yourdllpath

    其中/l:cs参数表示生成C#程序,/o:c:\表示输出到c盘根路径,/n:MyComm 表示修改命名空间为MyComm

     

    之后到输出盘(本例子为c:)会找到ICommService.Sinks.csICommService.Invokes.cs两个文件,将其拷贝到你的工作流项目根目录下并包含在项目中。

    添加对ClassLibrary1.dll的引用;

    重新编译一下你的工作流Host项目(最好在添加这两个类之前先编译一次保证没有其他错误),这时你会在工具箱中看到两个新控件,CallTheHostNotigyTheWorkflow如图:

    接下来我们要画一个工作流,例子如下:

    其中使用了我们用wca工具生成的CalltheHostNotigyTheWorkflow两个控件。

    选择callTheHost控件我们可以看见属性面板里面有一个param的属性,这正是在IcommService接口中定义的参数名称,点击“…”可以弹出属性绑定对话框,选择“绑定到新成员”“创建属性”输入“MyParam”如下设置:

     

    后台代码自动添加了如下属性:

    public static DependencyProperty MyParamProperty = DependencyProperty.Register("MyParam", typeof(System.String), typeof(WorkflowConsoleApplication2.Workflow1));

     

            [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]

            [BrowsableAttribute(true)]

            [CategoryAttribute("杂项")]

            public String MyParam

            {

                get

                {

                    return ((string)(base.GetValue(WorkflowConsoleApplication2.Workflow1.MyParamProperty)));

                }

                set

                {

                    base.SetValue(WorkflowConsoleApplication2.Workflow1.MyParamProperty, value);

                }

            }

     

    当流程执行到CallTheHost1控件时,就会调用Host中的CallTheHost方法的实现了,工作流中只需在此之前设置好“MyParam”的值即可,如在mycheck1控件中设置:

    MyParam = "我来自WorkFlowID="+this.WorkflowInstanceId.ToString();

     

    notigyTheWorkflow2控件的使用与CallTheHost1控件相似,在其属性中有一个“invoke”属性,这里输入一个方法名称后回车,如“WaitForHost”,程序会自动添加WaitForHost方法到代码中。notigyTheWorkflow2接口的参数MyEventArgs可以通过设置Msg属性来传值,操作与上面设置MyParam属性相同。

     

    上面已经在工作流中安置好了通讯接口,现在在Host程序中实现该通讯接口并调用;

    在工作流的Host程序中添加对ClassLibrary1.dll的引用,添加一个服务类,代码如下:

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

    using ClassLibrary1;

     

    namespace WorkflowConsoleApplication2

    {

         public class CommService:ICommService

         {

            //用于从WF中触发Host中的方法

            public void CallTheHost(string param)

            {

                Console.WriteLine("收到WF的消息:"+param);

               

            }

     

            //用于从Host中触发WF中的事件,这里的ExternalDataEventArgs可以自定义

            public event EventHandler<MyEventArgs> NotigyTheWorkflow;

     

            public void FireTheNotifyMethod(MyEventArgs arg)

            {

                NotigyTheWorkflow(null, arg);

            }

         }

    }

     

    然后我们在Host程序调用此服务:

    ExternalDataExchangeService dataservice = new ExternalDataExchangeService();

    workflowRuntime.AddService(dataservice);

     

    CommService service = new CommService();

    dataservice.AddService(service);

    ClassLibrary1.MyEventArgs arg = new ClassLibrary1.MyEventArgs(instance.InstanceId, "我来自Host");

    service.FireTheNotifyMethod(arg);

     

    如此便实现了HostWF之间的实时通讯。

     

    3.     使用WorkflowQueue来进行通讯

    2种方法中有一个NotigyTheWorkflow控件,它负责从Host里面触发事件并调用WF里面的一个方法,传递的数据放在EventArgs参数里面。现在介绍用WorkflowQueue对象来实现同样的功能。

     

    我们自定义一个活动,代码如下:

    using System;

    using System.Collections.Generic;

    using System.Workflow.Runtime;

    using System.Workflow.ComponentModel;

    using System.Workflow.ComponentModel.Design;

    using System.ComponentModel;

    using System.Text;

     

    namespace WorkflowConsoleApplication2

    {

        [ToolboxItem(typeof(ActivityToolboxItem))]

        [Description("用于读取数据的自定义活动")]

         public class MyRead:Activity

         {

            private string text;

            [Browsable(false)]

            public string Text

            {

                set { text = value; }

                get { return text; }

            }

     

            protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)

            {

                //获取WF队列服务

                WorkflowQueuingService qService = executionContext.GetService<WorkflowQueuingService>();

                //创建WF队列,第一个参数是队列的名称

                WorkflowQueue queue = qService.CreateWorkflowQueue(this.Name, true);

                //当收到外部数据后触发的事件

                queue.QueueItemAvailable += new EventHandler<QueueEventArgs>(queue_QueueItemAvailable);

                //重要!设置当前活动状态为“执行中”

                return ActivityExecutionStatus.Executing;

            }

     

            void queue_QueueItemAvailable(object sender, QueueEventArgs e)

            {

                ActivityExecutionContext context = sender as ActivityExecutionContext;

                WorkflowQueuingService qService = context.GetService<WorkflowQueuingService>();

                WorkflowQueue queue = qService.GetWorkflowQueue(this.Name);

                //获取WF队列中的数据

                text = (string)queue.Dequeue();

                //删除WF队列,参数为队列名称

                qService.DeleteWorkflowQueue(this.Name);

                //重要!此方法通知运行时本活动可以转移到closed状态

                context.CloseActivity();

            }

         }

    }

     

    介绍一下自定义活动的基本知识:只需要继承Activity或者其子类即可自定义活动控件,主要需要实现的方法是Execute,它有一个参数,可以获取当前运行的上下文,它需要返回一个ActivityExecutionStatus类型以便指示下一步怎么走,如果返回ActivityExecutionStatus.Executing,则表示本活动尚未结束不能转移到下一步。正常是返回ActivityExecutionStatus.Closed,表示当前活动结束,可以执行下一步活动。

     

    在这段示例里面,我们创建了WorkflowQueue对象,并添加一个事件绑定,以便当WorkflowQueue收到消息的时候执行一个方法,此方法里面需要调用context.CloseActivity()将本活动状态标识为ActivityExecutionStatus.Closed。这样WorkflowRuntime就会将活动从队列中卸载并执行下一个活动。

     

    然后看如何在Host里面传一个参数进来:

    string password = Console.ReadLine();

    instance.EnqueueItem("myRead1", password, null, null);

    只要在WorkflowInstance身上调用EnqueueItem方法就可以给指定队列传数据了,EnqueueItem方法第一个参数是队列的名字,必须跟CreateWorkflowQueue里面初始化的名字一样。这里可以多次调用EnqueueItem方法,以便传递多个数据进WF

     

    WF里面往Host里面传参数,可以在WF里面使用WorkflowQueue.Enqueue()方法;在Host里面使用instance.GetWorkflowQueueData()方法。

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 数的统计
    Java实现 蓝桥杯VIP 算法训练 和为T
    Java实现 蓝桥杯VIP 算法训练 友好数
    Java实现 蓝桥杯VIP 算法训练 连续正整数的和
    Java实现 蓝桥杯VIP 算法训练 寂寞的数
    Java实现 蓝桥杯VIP 算法训练 学做菜
    Java实现 蓝桥杯VIP 算法训练 暗恋
    Java实现 蓝桥杯VIP 算法训练 暗恋
    测试鼠标是否在窗口内,以及测试鼠标是否在窗口停留
    RichEdit 各个版本介绍
  • 原文地址:https://www.cnblogs.com/tuyile006/p/1564240.html
Copyright © 2011-2022 走看看