• 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()方法。

  • 相关阅读:
    MSSQL-sql server-视图简介
    MSSQL 如何采用sql语句 获取建表字段说明、字段备注、字段类型、字段长度
    mssql instead of 触发器应用一-创建只读视图(view)的方法
    DVWA-XSS学习笔记
    DVWA-命令执行学习笔记
    DVWA-暴力破解学习笔记
    kali权限提升之本地提权
    信息收集之主动信息收集(二)
    kali权限提升之配置不当提权与WCE
    linux 搭建squid代理服务器
  • 原文地址:https://www.cnblogs.com/tuyile006/p/1564240.html
Copyright © 2020-2023  润新知