一起学WF3.5【11】

来源:互联网 发布:奥塔哥大学 知乎 编辑:程序博客网 时间:2024/05/21 19:31

调用外部方法及工作流

我们启动工作流,然后从外部源中寻找并处理数据、返回处理后的数据给主应用程序在工作流使用场景中更为常见。

使用序列化技术,如.NET Remoting或XML Web服务,使用公开的对象在工作流和宿主应用程序之间互相传送,这些转换后的数据可在不同的进程或不同的主机传传送。但在工作流和宿主之间选择使用local通信进行。

创建ExternalDataService服务

工作流和宿主进行通信时,要使用队列和消息。

工作流内部进程通信

简单的通信WF使用abstraction layer作为宿主和工作流的缓冲。它是一个黑盒的Local Communication服务。由于传递的数据有其特殊性,所以你需要创建服务的一部分,即可以创建各种数据传输方法,使用它从宿主中发送数据,在工作流中接收数据。

这里存在一个对象或集合的共享问题。因为宿主和工作流在同一个应用程序域执行。因此引用类型的对象和集合就是通过引用而不是值进行传递。这样宿主应用程序和工作流实例在同一时间会访问和使用同一个对象,在多线程环境下这会产生数据并发访问错误。因此对于要并发访问的对象或集合,你可以考虑传递一个对象或集合的副本。这可以通过实现ICloneable接口,或考虑序列化后传递序列化后的版本。

写这种local service,把它插进工作流,打开连接发送数据,数据为任何类型,通信是双向的。从宿主应用程序角度说,接收数据等同于一个事件,发送数据就是在调用一个服务上的方法。

最终还要添加ExternalDataService服务到我们的工作流运行时中,它方便了工作流实例和宿主应用程序之间进行序列化的传输。

 

设计并实现工作流内部进程通信

首先设计一个ExternalDataService能够绑定的接口,这个接口将包含我们设计的一些方法,使用该接口中的方法,我们就可以来回传递数据。

接下来需要创建连接代码,它可以和ExternalDataService进行交互。它由两个类组成:一个connector类和一个serivce类。connector类管理数据管道,而service类被宿主和工作流用来进行数据交换。

我们使用wca.exe在由创建好的接口生成两个活动,用它们把该接口和工作流实例绑定。一个为invoker用来发送数据,一个为sink用于接收数据。创建好后,即可从工具箱把它们拖到工作流设计器上,它也和其它工作流一样进行工作。

从项目角度考虑,如果为宿主应用程序创建一个或一组项目,为前面提到的接口和连接桥创建另一个项目,为工作流创建一个项目。这样可以方便的在宿主和工作流中添加对该接口和桥接类的引用。

有了这些程序集,我们使用ExternalDataService连通工作流和宿主之间的通信。

 

一个例子 -- 机动车数据检查程序

本例是一个Windows Forms应用程序,程序显示了驾驶员的机动车数据。

创建项目,名为MVDataChecker。设计界面如下,包括一下下拉列表,两个ListView控件,两个按钮,一个picturebox控件。下拉列表中显示司机姓名,点击检索按钮,在ListView中显示该驾驶员的机动车信息。它由一个工作流实例来对该驾驶员的机动车信息进行检索。

点击检索按钮时,你会初始化一个新的工作流实例,该按钮和下拉列表框控件被禁用,底部的picture box控件显示查找进度。

工作流实例完成了查找后,它使用一个活动激发一个事件,宿主应用程序会截获该事件,该事件把数据已准备好的消息通知给该宿主应用程序。Windows窗体的ListView控件不能绑定DataTable对象,因此我们检索后把数据一条条插入控件中。

假如你在查询过程中退出该应用程序,正在执行的工作流程序会被异常终止。

 

添加接口代码使它能激发“数据已准备好”事件:

该接口需要我们自已添加,它基于工作流实例和宿主应用程序之间进行通信的数据上。对于本例,工作流从各个源数据中检索驾驶员信息,然后把这些信息整理为一个数据结构:带多个表的DataSet,一个表是车辆标识信息,一个是驾驶员违规信息,这里我们使用虚拟的数据。

你要把驾驶员的名字传入工作流实例中,该工作流实例依照它查找驾驶员和车辆信息。在获取了这些数据后,工作流实例通知宿主应用程序数据已准备好,宿主读取并显示这些信息。

因此我们的接口只需要定义一个单一的方法:MVDataUpdate。为它传入获取的DataSet。

创建一个工作流数据通信接口:

解决方案中有三个项目,其中的MVDataService项目(其它一个是工作流项目,一个是宿主)中,有名为IMvDataService的接口,它定义如下:

public interface IMVDataService

{

         voidMVDataUpdate(DataSet mvData);

}

我们还需要添加一个属性以使它能够被WF使用,即ExternalDataChange特性。在接口定义上添加[ExternalDataChange]。

 

使用ExternalDataEventArgs

宿主和工作流之间使用事件进行通信。WF使用异步方式,在数据准备好后激发事件。宿主应用程序捕获这些事件然后读出数据。

我们需要创建一个自定义事件参数的类,前面我们使用System.EventArgs作为基类。WF外部数据事件需带一个和上述不同的参数作为基类以使用工作流实例承载实例ID。我们应用的基类是ExternalDataEventArgs。另外,我们还需要提供一个以该实例的ID作为参数的基本构造器,还需要Serializable特性标记类,以表明是可序列化的。

创建工作流数据事件参数类

代码如下:

[Serializable]

public class MVDataAvailableArgs :ExternalDataEventArgs

{

        

}

 

添加构造器,把ID传递给基类:

public MVDataAvailableArgs(Guid instanceId): base(instanceId)

{

 

}

完整的事件参数类:

 

 

创建外部数据服务

我们需要为外部数据服务创建桥接代码,以使宿主访问到工作流传递过来的数据。创建一个仅支持工作流到宿主的单向桥接版本。连接桥有两部分:connector实现了前边的接口,service激发事件并提供数据读取方法。这么做的原因是进出工作流实例的事件都由工作流运行时代理,宿主应用程序只能把数据发送工作流运行时。

桥接类包含一个字段,工作流使用要被传递的数据填充该字段。每一个工作流实例可能会返回不同的数据,连接类实现在宿主这边的接口,以及不间断的保持这些数据。当宿主请求该数据连接时,连接类根据工作流实例ID确定应正确返回的DataSet是否正确到达。

服务类为你处理一些任务。首先是使用工作流运行时注册该ExternalDataService以便使宿主和工作流之间可通信。它维护一个连接类的单例副本,并把自己做为服务的提供者绑定到该连接类。它也是一个工厂角色,确保我们有且仅有一个连接类的实例。

 

创建桥接器类

在MVDataService项目中,创建MVDataConnector类并添加如下代码:

publicsealed class MVDataConnector : IMVDataService

{

         private DataSet _dataValue = null;

         private static WorkflowMVDataService_service = null;

         private static object _syncLock = newobject();

        

}

字段_dataValue用来容纳工作流实例产生的数据。字段_service用来容纳数据服务对象的单一实例。_syncLock对象仅仅用来进行线程的同步。

添加一个static属性来访问该对象的单一实例:

publicstatic WorkflowMVDataService MVDataService

        {

            …代码略

        }

添加一个属性访问DataSet:

publicDataSet MVData

        {

            get{ return _dataValue; }

        }

实现IMVDataService的MVDataUpdate方法:

publicvoid MVDataUpdate(DataSet mvData)

        {

            //Assign the field for later recall

           _dataValue = mvData;

 

            //Raise the event to trigger host read

           _service.RaiseMVDataUpdateEvent();

        }

 

创建桥接服务类

在MVDataService项目中创建WorkflowMVDataService类,定义如下字段:

         static WorkflowRuntime_workflowRuntime = null;

         staticExternalDataExchangeService _dataExchangeService = null;

         staticMVDataConnector _dataConnector = null;

         staticobject _syncLock = new object();

 

         privateevent EventHandler<MVDataAvailableArgs> MVDataUpdate;

         privateGuid _instanceID = Guid.Empty;

 

要有从类的外部访问_instanceID的能力,添加如下属性:

         public GuidInstanceID

         {

                   get{ return _instanceID; }

                   set{ _instanceID = value; }

         }

需要添加一个静态方法创建本类的实例。它确保所有的事情都已完成,如ExternalData  Service服务已插入到工作流运行时中;添加刚才建好的桥接器类等。

publicstatic WorkflowMVDataService CreateDataService(Guid instanceID, WorkflowRuntimeworkflowRuntime)

        {

            …代码略

        }       

添加静态方法返回桥接服务实例:

         public staticWorkflwoMVDataService GetRegisteredWorkflowDataService(Guid instanceID)

         {

                   …代码略

         }

为确保桥接器对象和桥接服务对象间不会造成循环引用,添加构造器和析构器:

privateWorkflowMVDataService(Guid instanceID)

        {

           _instanceID = instanceID;

           MVDataConnector.MVDataService = this;

        }

 

       ~WorkflowMVDataService()

        {

            //Clean up

           _workflowRuntime = null;

           _dataExchangeService = null;

           _dataConnector = null;

        }

 

为使工作流运行时,在引入ExternalDataService后,具有读取数据并返回给宿主应用程序的能力,添加Read方法:

publicDataSet Read()

        {

           return _dataConnector.MVData;

        }

 

为激发数据更新事件添加一个方法,工作流使用这个方法为宿主发送一个通知,告诉它要挑选的数据已经获取:

publicvoid RaiseMVDataUpdateEvent()

        {

            …代码略

        }

 

创建和使用自定义外部数据服务活动:

CallExternalMethod活动可接受一个接口及该接口所支持的方法,并来调用方法。你可使用wca.exe工具来创建一个发送数据到宿主应用程序的活动。

编译MVDataService项目并生成程序集,在cmd里进入程序集目录,用wca.exe的绝对路径,如”C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\Wca.exe” MVDataService.dll,注意带引号。

将生成的IMVDataService.Invokes.cs可命名为MVDataUpdate.cs。

新建一个顺序工作流库项目,将MVDataUpdate.cs复制进去,编译。可以VS的工具箱中看到这个MVDataUpdate活动,它的基类是CallExternalMethod,它的作用是触发对工作流执行环境的外部调用。

在项目的工作流设计器中,添加一个Delay活动用来模拟处理数据的等待时间,一个Code活动用来创建并填充一个基于驾驶员姓名的DataSet。

Code活动的执行方法命名为GenerateMVData生成DataSet,在最后将它赋给MVDataUpdate的mvData属性。       


东西有些多,相关的完整代码已上传至附件中。下节我们将完成宿主应用程序以使用整个系统运行起来。  

原创粉丝点击