版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、CorrelationParameter特性假如你考虑在单一宿主应用程序的环境下可能会出现多个工作流实例的话,你可能将会发现传送数据的事件和方法也会传送某种唯一的标识符。一个订单处理系统可能要传送一个客户ID,或者一个包装系统可能要传送一个批号。这个唯一标识符的类型是确定数据唯一实例的完美候选,事实上,的确如此。当你在你的通信接口中设计方法和事件的时候,你也要为其设计一个数据相关ID的签名。数据相关ID在所有的空间和时间情况下并不保证必须是唯一的。但是,假如它不是一个Guid,它就必定要保证在工作流实例执行期间要被唯一地使用。也许令人惊讶的是,假如你创建了两个相关的工作流实例,它们使用了同一个
2、参数值(即创建了两个使用了同一客户ID的工作流)在同一时间运行的话,这并不是一个错误。关联仅仅和使用了单一的关联参数值的单一的工作流实例联系起来。使用一个关联参数值创建的工作流在调用方法和事件进行数据交换时使用了不同的关联值的地方则是错误,在这些地方WF可帮助你防止产生错误。你要在你的接口定义中包括CorrelationParameter特性来通知WF哪些方法参数要承载这个数据关联ID值(把它们放在ExternalDataExchange特性的旁边)。当数据传递的时候WF能够检查参数的内容。例如,假如你的逻辑试图混淆客户或者(包装)批号的话,WF将抛出System.Workflow.Activ
3、ity.EventDeliveryFailedException。这个异常对你很有帮助,因为它指出了你的处理逻辑部分存在明确的不匹配的数据。例如,一个客户却为其它客户买单,显而易见,这种结果是不期望发生的。假如你接收到一个异常,你就需要去检查你应用程序中不正确的逻辑处理操作。CorrelationParameter特性在它的构造器中接收一个字符串。这个字符串代表了你的接口所使用的包含了唯一ID的参数的名称。假如你要为某一指定的方法进行对该参数进行重命名,你就可通过使用CorrelationAlias参数来为这些事件和方法进行参数的重命名。你将在本章的稍后部分读到关于这个参数的更多知识。Corr
4、elationInitializer特性当数据通信开始进行的时候,WF也需要初始化相关令牌。为了方便地完成这个工作,你可在方法或事件上加入CorrelationInitializer特性来进行数据通信,这可能有多个事件或方法需要添加这个特性。在执行该方法或事件前进行相关数据的来回传送的任何企图其结果都是产生一个异常,并会在这个异常中标记该关联初始化器。CorrelationAlias特性当你创建相关的服务后,CorrelationParameter特性通过名称来识别出被用来传送数据相关标识符的方法参数。对于你的接口方法来说,这意味着你必须有一个方法参数使用了和相关参数名相同的名称来命名。假如你
5、的代理(delegate或称委托)以相关参数定义在代理中的方式创建的话,这没有任何问题。但是这对于事件来说就不适用了。当你使用一个包含了事件参数的代理并且这些事件参数要传送相关参数的时候问题出现了。例如,设想你的相关参数被命名为customerID,然后考虑一下下面这个代理:delegatevoidMyEventHandler(objectsender,MyEventArgse);假如使用了这个委托的事件被放进你的通信接口中后,customerID参数没有在事件处理程序中出现,当你执行你的工作流的时候,WF会抛出一个异常来指出关联被错误地使用。但是,假如MyEventArgs有一个包含了客户I
6、D的属性的话,你就能使用CorrelationAlias特性来指出它。对本例子而言,假如MyEventArgs的客户ID属性被命名为CustomerID的话,相关参数的别名(alias)就将是e.CustomerID。一个重要的事情是要牢记住一旦你为某一单一的工作流实例初始化了一个相关数据路径的话,你就不能在该工作流实例的生命周期中改变该数据的关联ID而不会出现错误。例如,一旦你和一个用客户的ID号作为联系的工作流实例通信的话,以后你就不能使用其他客户的ID号来和同一个工作流实例进行数据通信。意思就是,就像把信息插入进一个数据表中新行的时候,假如你的处理过程涉及到创建客户的ID号,你就需要预先
7、生成该客户的ID号。你不能让数据库为你生成它们,或者开始默认为“空”(“empty”)然后在后来使用一个新生成的ID号,因为这样你的通信过程就会在没有客户ID的情况下被初始化。提到的这些ID号如果有所不同的话,你的工作流就会抛出一个异常。创建相关工作流在本章中我介绍了关联的概念并只提到了三个特性。这就是所有的东西吗?对的。但是我们的本地服务变得更加复杂,因为我们必须考虑不同的数据流。记住,本地通信服务在工作流运行时中是一个单例(singleton)服务,因此各个不同的工作流实例所请求的全部数据都必须通过这个本地通信服务。该服务不可避免地必须知悉各个工作流实例和关联参数的细节以便当宿主从一个指定
8、的工作流请求数据的时候,该服务能返回正确的数据。备注:你怎样架构你的本地通信服务取决于你。在本章的稍后部分我将为了展示我是怎么创建它们的,但最终没有任何规则要让你也必须像我所做的一样去创建你的服务。唯一的要求是你要能从你的服务中返回正确的相关数据。为了让你能理解下面更大的图片,我将首先介绍你将使用的应用程序并解释它为什么使用关联。相关工作流的典型例子是一个订单处理系统,它使用唯一的客户ID号去了解客户的订单信息。本章的示例应用程序模拟了一个货运公司可能用来跟踪其车辆的应用程序。今天,许多长途卡车都装备了全球定位系统(GPS),它能把卡车的位置报告给运输公司。无论卡车发生了什么,你都能跟踪它并监
9、控其对于目的地的进展状况。这个范例模拟了这种类型的跟踪应用程序,它的用户界面如图17-1所示。图中展示了四个卡车,它们到达各个不同的终点(通过活动卡车列表显示出来)。所有卡车自身都是动态的,它们从起点移动到终点。当它们到达各自的终点时,它们会被从活动卡车的列表中移除。图17-1TruckTracker应用程序用户界面你会看到每一辆卡车都由它自己的工作流实例支持。工作流定时、异步地对卡车的地理位置进行更新。当更新进行的时候,工作流和应用程序会为计算新的坐标进行通信,然后可视化地在用户界面中对卡车的位置进行更新。当然,我们正模拟的是GPS的接受效果,所模拟卡车的移动速度远远大于实际的卡车能够达到的
10、速度。(运行本范例4天时间后才去看卡车是否真正从加利福尼亚抵达新泽西是非常愚蠢的行为。)应用程序真正关键的地方是当和宿主应用程序进行数据通信时使用了相关的工作流实例。卡车按照指定的路线向它们各自的终点前进,它会在地图上穿越其它城市。当你点击“Add Truck”后,通过如下图17-2所显示的对话框,你可以选择卡车的路线。卡车的路线都保存在一个XML文件中,当应用程序加载时把它们读出来。例如,此行从萨克拉门托到特伦顿,卡车将穿过凤凰城,圣菲,奥斯汀,以及塔拉哈西。图17-2 “Add Truck”对话框主应用程序现在已经为你完成了,剩下的任务是要完成对应的服务和工作流。我们首先将创建服务接口。为
11、你的应用程序添加相关通信接口1.本范例同样为你提供了两个版本:完整版本和非完整版本。你需要下载本章源代码,打开“TruckTracker”目录中的解决方案。2.本解决方案包含两个项目:TruckTracker(主应用程序)和TruckService。在Visual Studio的解决方案资源管理器中找到TruckService项目,打开ITruckService.cs文件准备进行编辑。3.在接口的大括号中,添加下面这些代码:/ Workflow to host communicationCorrelationInitializervoid ReadyTruck(Int32 truckID, I
12、nt32 startingX, Int32 startingY);void UpdateTruck(Int32 truckID, Int32 X, Int32 Y);void RemoveTruck(Int32 truckID);/ Host to workflow communicationCorrelationAlias(truckID, e.TruckID)event EventHandler CancelTruck;CorrelationInitializerCorrelationAlias(truckID, e.TruckID)event EventHandler AddTruck;
13、4.正好在ExternalDataExchange特性的前面(你会发现它用来对接口进行修饰),插入CorrelationParameter特性。CorrelationParameter(truckID)5.保存本文件。回头看看你在第3步所添加的代码,它也被复制到了列表17-1中,你能看到在本章中所讨论过的每一个特性。名称为truckID的方法参数传递一个唯一的卡车标识符,它存在于该接口的所有方法中。然后,CorrelationParameter特性通知WF这个方法参数的作用是为了关联。列表17-1 ITruckService.cs的完整代码ITruckService接口的完整代码using S
14、ystem;using System.Collections.Generic;using System.Text;using System.Workflow.ComponentModel;using System.Workflow.Activities;namespace TruckServiceCorrelationParameter(truckID)ExternalDataExchangepublic interface ITruckService/ Workflow to host communicationCorrelationInitializervoid ReadyTruck(In
15、t32 truckID, Int32 startingX, Int32 startingY);void UpdateTruck(Int32 truckID, Int32 X, Int32 Y);void RemoveTruck(Int32 truckID);/ Host to workflow communicationCorrelationAlias(truckID, e.TruckID)event EventHandler CancelTruck;CorrelationInitializerCorrelationAlias(truckID, e.TruckID)event EventHan
16、dler AddTruck;AddTruck和CancelTruck两个事件使用了一个CorrelationAlias特性来把关联参数的名称由truckID重新指定为e.TruckID,因为事件参数(arguments)为这些事件携带了关联标识符。对于本范例来说使用的是e.TruckID,但是任何事件参数(argument)都能被用来携带关联参数。也就是说,你能把truckID的别名指定为任何也携带了关联值到工作流中去的参数。在这个接口中有两种来对关联机制进行初始化的方式:工作流可以调用ReadyTruck,或者宿主应用程序调用AddTruck事件。任何一个都会开始进行相关通信(correla
17、ted communications),因为两者都使用了CorrelationInitializer特性进行修饰。在此之前调用任何其它的方法或者事件进行初始化其结果是工作流运行时产生异常。服务项目通常都带有本地通信服务,这个范例的应用程序也并没有什么不同。因为连接器(connector)类TruckServiceDataConnector由ITruckService派生,因此现在是该完成它的时候了。完成关联的数据连接器(correlated data connector)1.再次回到TruckService项目,找到TruckServiceDataConnector.cs文件并打开它准备进行编
18、辑。2.TruckServiceDataConnector类现在是空的,但是它明显派生自ITruckService。因此,你至少要把接口中的方法和事件添加到这个类中。但是在你完成这些之前,我们先添加一些需要的代码。首先,正好在这个类的左大括号后面添加下面的字段。protected const string KeyFormat = 0.Truck_1;protected static Dictionary _dataValues = new Dictionary();protected static Dictionary _dataServices =new Dictionary();priva
19、te static object _syncLock = new object();3.因为数据连接器需要与数据项保存联系并且在工作流运行时中是一个单例模式(singleton),因此我们将添加一对静态方法,它们用来对数据服务进行注册并对已经注册的数据服务进行检索。WorkflowTruckTrackingDataService方法和RegisterDataService方法public static WorkflowTruckTrackingDataService GetRegisteredWorkflowDataService(Guid instanceID,Int32 truckID)s
20、tring key = String.Format(KeyFormat, instanceID, truckID);WorkflowTruckTrackingDataService serviceInstance = null;if (_dataServices.ContainsKey(key)/ Return the appropriate data serviceserviceInstance = _dataServiceskey;return serviceInstance;public static void RegisterDataService(WorkflowTruckTrack
21、ingDataService dataService)string key = String.Format(KeyFormat, dataService.InstanceID.ToString(), dataService.TruckID);lock (_syncLock)_dataServices.Add(key, dataService);4.当一个新的工作流实例被启动的时候,在主应用程序中都要对数据服务进行注册(每个工作流实例对应一个数据服务),一旦数据服务被注册后,它就在数据连接器中保存关联数据。我们需要有一个去检索该数据的方式。以前,我们使用的是一个属性,但是对于我们现在来说这已经不
22、适用了,因为我们必须传入工作流实例ID和关联值(在本例中是一个卡车的标识符)二者。为了检索该数据,在静态的注册方法(RegisterDataService方法)的下面添加如下这个方法:RetrieveTruckInfo方法public string RetrieveTruckInfo(Guid instanceID, Int32 truckID)string payload = String.Empty;string key = String.Format(KeyFormat, instanceID, truckID);if (_dataValues.ContainsKey(key)paylo
23、ad = _dataValueskey;return payload;5.有了这最后的一个方法后,我们现在就去添加来自于ITruckService接口的方法。它们的位置紧跟在前一步骤所添加的检索数据的方法下面。继承自ITruckService接口的方法/ Workflow to host communication methodspublic void ReadyTruck(Int32 truckID, Int32 startingX, Int32 startingY)/ Pull correlated serviceWorkflowTruckTrackingDataService servi
24、ce =GetRegisteredWorkflowDataService(WorkflowEnvironment.WorkflowInstanceId,truckID);/ Place data in correlated storeUpdateTruckData(service.InstanceID, truckID, startingX, startingY);/ Raise the event to trigger host activityif (service != null)service.RaiseTruckLeavingEvent(truckID, startingX, sta
25、rtingY);public void UpdateTruck(Int32 truckID, Int32 X, Int32 Y)/ Pull correlated serviceWorkflowTruckTrackingDataService service =GetRegisteredWorkflowDataService(WorkflowEnvironment.WorkflowInstanceId,truckID);/ Update data in correlated storeUpdateTruckData(service.InstanceID, truckID, X, Y);/ Ra
26、ise the event to trigger host activityif (service != null)service.RaiseRouteUpdatedEvent(truckID, X, Y);public void RemoveTruck(Int32 truckID)/ Pull correlated serviceWorkflowTruckTrackingDataService service =GetRegisteredWorkflowDataService(WorkflowEnvironment.WorkflowInstanceId,truckID);/ Remove t
27、ruck from correlated storestring key = String.Format(KeyFormat, service.InstanceID, truckID);if (_dataValues.ContainsKey(key)/ Remove it_dataValues.Remove(key);/ Raise the event to trigger host activityif (service != null)service.RaiseTruckArrivedEvent(truckID);6.在ITruckService中的方法的后面是一些事件,因此也要添加它们,
28、它们的位置在你第5步所添加的方法的下面。继承自ITruckService接口的事件/ Host to workflow eventspublic event EventHandler CancelTruck;public void RaiseCancelTruck(Guid instanceID, Int32 truckID)if (CancelTruck != null)/ Fire eventCancelTruck(null, new CancelTruckEventArgs(instanceID, truckID);public event EventHandler AddTruck;p
29、ublic void RaiseAddTruck(Guid instanceID, Int32 truckID, Int32 routeID)if (AddTruck != null)/ Fire eventAddTruck(null, new AddTruckEventArgs(instanceID, truckID, routeID);7.回头看看在第5步中插入的方法,你会看到一个用来把关联数据插入到对应的数据字典中去的方法(UpdateTruckData)。数据本身必须被转换为XML,因此以其把下面这些代码都扩散到上面的三个方法中,那还不如添加一个UpdateTruckData方法,然后
30、把它们都放在UpdateTruckData方法中。现在就添加该方法,把它放在你前面添加的事件的下面:UpdateTruckData方法protected Truck UpdateTruckData(Guid instanceID, Int32 truckID, Int32 X, Int32 Y)string key = String.Format(KeyFormat, instanceID, truckID);Truck truck = null;if (!_dataValues.ContainsKey(key)/ Create new trucktruck = new Truck();tru
31、ck.ID = truckID;else/ Pull existing truckstring serializedTruck = _dataValueskey;StringReader rdr = new StringReader(serializedTruck);XmlSerializer serializer = new XmlSerializer(typeof(Truck);truck = (Truck)serializer.Deserialize(rdr);/ Update valuestruck.X = X;truck.Y = Y;/ Serialize valuesStringB
32、uilder sb = new StringBuilder();using (StringWriter wtr = new StringWriter(sb)XmlSerializer serializer = new XmlSerializer(typeof(Truck);serializer.Serialize(wtr, truck);/ Ship the data back_dataValueskey = sb.ToString();return truck;8.保存该文件。列表17-2中列出了TruckServiceDataConnector类的完整代码。在强调一次,这个类的作用是保存来
33、自各个工作流实例的关联数据。该数据被存储到一个Dictionary对象中,键值对把工作流实例标识符和卡车标识符关联起来。该数据连接器类在工作流运行时中是一个单例服务。备注:你可能会问:为什么数据要转换为XML而不直接使用数据对象本身?这是因为,当在工作流和宿主之间来回传送数据的时候,WF并不对对象进行序列化。因此,并不会创建对象的副本(毫无疑问是为了提高性能)。对象的传递是通过引用实现的,由此工作流和宿主都在同一个对象上工作。假如你不想这样(注:指对象引用),又不想使用本范例中的方法(注:指转换为XML)的话,你就可以对对象进行序列化或者实现ICloneable来传递对象的副本。假如这种行为(
34、注:指对象引用)并不影响你的设计,你就不需要再去做任何事情而直接(在工作流和宿主之间)来回通过引用的方式去传递你的对象。虽然如此,也要牢记你的对象将会被两个不同的线程所共享。列表17-2 TruckServiceDataConnector.cs的完整代码TruckServiceDataConnector类的完整代码using System;using System.Collections.Generic;using System.Text;using System.Threading;using System.Workflow.Activities;using System.Workflow.
35、Runtime;using System.Xml;using System.Xml.Serialization;using System.IO;namespace TruckServicepublic class TruckServiceDataConnector : ITruckServiceprotected const string KeyFormat = 0.Truck_1;protected static Dictionary _dataValues = new Dictionary();protected static Dictionary _dataServices = new
36、Dictionary();private static object _syncLock = new object();public static WorkflowTruckTrackingDataService GetRegisteredWorkflowDataService(Guid instanceID, Int32 truckID)string key = String.Format(KeyFormat, instanceID, truckID);WorkflowTruckTrackingDataService serviceInstance = null;if (_dataServi
37、ces.ContainsKey(key)/ Return the appropriate data serviceserviceInstance = _dataServiceskey; / ifreturn serviceInstance;public static void RegisterDataService(WorkflowTruckTrackingDataService dataService)string key = String.Format(KeyFormat, dataService.InstanceID.ToString(), dataService.TruckID);lo
38、ck (_syncLock)_dataServices.Add(key, dataService); / lockpublic string RetrieveTruckInfo(Guid instanceID, Int32 truckID)string payload = String.Empty;string key = String.Format(KeyFormat, instanceID, truckID);if (_dataValues.ContainsKey(key)payload = _dataValueskey; / ifreturn payload;/ Workflow to
39、host communication methodspublic void ReadyTruck(Int32 truckID, Int32 startingX, Int32 startingY)/ Pull correlated serviceWorkflowTruckTrackingDataService service = GetRegisteredWorkflowDataService(WorkflowEnvironment.WorkflowInstanceId, truckID);/ Place data in correlated storeUpdateTruckData(servi
40、ce.InstanceID, truckID, startingX, startingY);/ Raise the event to trigger host activityif (service != null)service.RaiseTruckLeavingEvent(truckID, startingX, startingY); / ifpublic void UpdateTruck(Int32 truckID, Int32 X, Int32 Y)/ Pull correlated serviceWorkflowTruckTrackingDataService service = G
41、etRegisteredWorkflowDataService(WorkflowEnvironment.WorkflowInstanceId, truckID);/ Update data in correlated storeUpdateTruckData(service.InstanceID, truckID, X, Y);/ Raise the event to trigger host activityif (service != null)service.RaiseRouteUpdatedEvent(truckID, X, Y); / ifpublic void RemoveTruc
42、k(Int32 truckID)/ Pull correlated serviceWorkflowTruckTrackingDataService service = GetRegisteredWorkflowDataService(WorkflowEnvironment.WorkflowInstanceId, truckID);/ Remove truck from correlated storestring key = String.Format(KeyFormat, service.InstanceID, truckID);if (_dataValues.ContainsKey(key)/ Remove it_dataValues.Remove(key); / if/ Raise the event to trigger host activityif (service != null)service.RaiseTruckArrive
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 新疆警察学院《学前儿童五大领域教育及活动设计》2023-2024学年第一学期期末试卷
- 2024年度市场部计划(33篇)
- 《工程档案管理工作》课件
- 《私营业主能否入党》课件
- 安保顾问服务合同范例
- 酒店住房结账合同范例
- 中介房屋代卖合同范例
- 手机产品包销合同范例
- 要签返聘合同范例
- 食品供应服务合同范例
- 注塑产品成本计算
- 积分上链方案
- 国家开放大学电大《小学语文教学研究》形考任务3-4试题及答案
- 通用劳务合同Word模板下载(多份)
- 第七讲 磁电选
- 英语主格宾格及其练习题
- 三七种植项目可行性方案
- 国内外河湖生态保护与修复技术标准进展综述
- 室内墙面喷涂与涂饰
- Unit2HowoftendoyouexerciseSectionA(1a-2d)教案人教版英语八年级上册
- 光伏电站事故处理规程
评论
0/150
提交评论