工作流是将程序(实际上就是业务)流程独立出来,建立健壮的可伸缩的交互式的流程管理。其基本任务是保证每一个步骤必须严格地执行且仅可以执行一遍。
WF和其宿主(Host)之间必须能够进行通讯,不然就失去了“交互性”,这里介绍三种方法。
如在workflow1中有定义两个属性:
private string inputParam;
private bool outputParam;
public string InputParam
{
set { inputParam = value; }
}
public bool OutputParam
{
get { return outputParam; }
}
如何从Host传数据到WF:
使用Dictionary<string, object>对象可以传参数给WorkflowRuntime的CreateWorkflow方法,这种方法可以在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:
在WorkflowRuntime的WorkflowCompleted事件里面有一个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的公共属性名称。
这种方法是比较重要的,因为前面所讲传参数的方法只能在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.cs和ICommService.Invokes.cs两个文件,将其拷贝到你的工作流项目根目录下并包含在项目中。
添加对ClassLibrary1.dll的引用;
重新编译一下你的工作流Host项目(最好在添加这两个类之前先编译一次保证没有其他错误),这时你会在工具箱中看到两个新控件,CallTheHost和NotigyTheWorkflow如图:
接下来我们要画一个工作流,例子如下:
其中使用了我们用wca工具生成的CalltheHost和NotigyTheWorkflow两个控件。
选择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);
如此便实现了Host和WF之间的实时通讯。
第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()方法。
转载于:https://www.cnblogs.com/tuyile006/archive/2009/09/10/1564240.html
相关资源:数据结构—成绩单生成器