首页 > 编程开发 > Objective-C编程 >
-
WF从入门到精通之关联及本地主机通信下
7.在类的构造器的下面,添加下面的读取关联数据的方法:
public string Read()
{
return _dataConnector.RetrieveTruckInfo(InstanceID, TruckID);
}
8.最后添加事件实现的代码:
事件实现的代码
public void RaiseTruckLeavingEvent(Int32 truckID,
Int32 startingX,
Int32 startingY)
{
if (_workflowRuntime == null)
_workflowRuntime = new WorkflowRuntime();
// Loads persisted workflow instances.
_workflowRuntime.GetWorkflow(_instanceID);
if (TruckLeaving != null)
{
TruckLeaving(this, new TruckActivityEventArgs(_instanceID,
truckID,
startingX,
startingY));
} // if
}
public void RaiseRouteUpdatedEvent(Int32 truckID,
Int32 X,
Int32 Y)
{
if (_workflowRuntime == null)
_workflowRuntime = new WorkflowRuntime();
// Loads persisted workflow instances.
_workflowRuntime.GetWorkflow(_instanceID);
if (RouteUpdated != null)
{
RouteUpdated(this, new TruckActivityEventArgs(_instanceID,
truckID,
X, Y));
} // if
}
public void RaiseTruckArrivedEvent(Int32 truckID)
{
if (_workflowRuntime == null)
_workflowRuntime = new WorkflowRuntime();
// Loads persisted workflow instances.
_workflowRuntime.GetWorkflow(_instanceID);
if (TruckArrived != null)
{
TruckArrived(this, new TruckActivityEventArgs(_instanceID,
truckID));
} // if
}
9.保存该文件并编译该TruckService项目。如果存在编译错误,请纠正任何出现的编译错误。
服务类的代码就全部完成了,列表17-3展示了该类的完整代码。TruckSercie本地通信服务可以准备使用了。我们还没有一个工作流来使用该服务。我们也需要使用可信赖的wca.exe工具来为我们创建自定义的CallExternalMethod和HandleExternalEvent活动。
列表17-3 WorkflowTruckTrackingDataService.cs的完整代码
WorkflowTruckTrackingDataService类的完整代码
using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.Activities;
using System.Workflow.Runtime;
namespace TruckService
{
public class WorkflowTruckTrackingDataService
{
private static WorkflowRuntime _workflowRuntime = null;
private static ExternalDataExchangeService _dataExchangeService = null;
private static TruckServiceDataConnector _dataConnector = null;
private static object _syncRoot = new object();
public event EventHandler<TruckActivityEventArgs> TruckLeaving;
public event EventHandler<TruckActivityEventArgs> RouteUpdated;
public event EventHandler<TruckActivityEventArgs> TruckArrived;
private Guid _instanceID = Guid.Empty;
public Guid InstanceID
{
get { return _instanceID; }
set { _instanceID = value; }
}
private Int32 _truckID = -1;
public Int32 TruckID
{
get { return _truckID; }
set { _truckID = value; }
}
public static WorkflowTruckTrackingDataService CreateDataService(Guid instanceID, WorkflowRuntime workflowRuntime, Int32 truckID)
{
lock (_syncRoot)
{
// If we're just starting, save a copy of the workflow runtime reference
if (_workflowRuntime == null)
{
_workflowRuntime = workflowRuntime;
} // if
// If we're just starting, plug in ExternalDataExchange service
if (_dataExchangeService == null)
{
_dataExchangeService = new ExternalDataExchangeService();
_workflowRuntime.AddService(_dataExchangeService);
} // if
// Check to see if we have already added this data exchange service
TruckServiceDataConnector dataConnector = (TruckServiceDataConnector)workflowRuntime.GetService(typeof(TruckServiceDataConnector));
if (dataConnector == null)
{
_dataConnector = new TruckServiceDataConnector();
_dataExchangeService.AddService(_dataConnector);
} // if
else
{
_dataConnector = dataConnector;
} // else
// Pull the service instance we registered with the connection object
return WorkflowTruckTrackingDataService.GetRegisteredWorkflowDataService(instanceID, truckID);
} // lock
}
public static WorkflowTruckTrackingDataService GetRegisteredWorkflowDataService(Guid instanceID, Int32 truckID)
{
lock (_syncRoot)
{
WorkflowTruckTrackingDataService workflowDataService = TruckServiceDataConnector.GetRegisteredWorkflowDataService(instanceID, truckID);
if (workflowDataService == null)
{
workflowDataService = new WorkflowTruckTrackingDataService(instanceID, truckID);
TruckServiceDataConnector.RegisterDataService(workflowDataService);
} // if
return workflowDataService;
} // lock
}
private WorkflowTruckTrackingDataService(Guid instanceID, Int32 truckID)
{
this._instanceID = instanceID;
this._truckID = truckID;
}
~WorkflowTruckTrackingDataService()
{
// Clean up
_workflowRuntime = null;
_dataExchangeService = null;
_dataConnector = null;
}
public string Read()
{
return _dataConnector.RetrieveTruckInfo(InstanceID, TruckID);
}
public void RaiseTruckLeavingEvent(Int32 truckID, Int32 startingX, Int32 startingY)
{
if (_workflowRuntime == null)
_workflowRuntime = new WorkflowRuntime();
_workflowRuntime.GetWorkflow(_instanceID); // loads persisted workflow instances
if (TruckLeaving != null)
{
TruckLeaving(this, new TruckActivityEventArgs(_instanceID, truckID, startingX, startingY));
} // if
}
public void RaiseRouteUpdatedEvent(Int32 truckID, Int32 X, Int32 Y)
{
if (_workflowRuntime == null)
_workflowRuntime = new WorkflowRuntime();
_workflowRuntime.GetWorkflow(_instanceID); // loads persisted workflow instances
if (RouteUpdated != null)
{
RouteUpdated(this, new TruckActivityEventArgs(_instanceID, truckID, X, Y));
} // if
}
public void RaiseTruckArrivedEvent(Int32 truckID)
{
if (_workflowRuntime == null)
_workflowRuntime = new WorkflowRuntime();
_workflowRuntime.GetWorkflow(_instanceID); // loads persisted workflow instances
if (TruckArrived != null)
{
TruckArrived(this, new TruckActivityEventArgs(_instanceID, truckID));
} // if
}
}
}
创建关联数据交换的工作流
在本例中创建的这个工作流和你以前创建的工作流项目是有所不同的。在Visual Studio的解决方案资源管理器中简单地右键点击TruckTracker解决方案的名称,然后选择“添加”,选择“新建项目”。当“添加新项目”对话框打开后,如果Visual C#树状控件节点没有展开的话,就展开Visual C#树状控件节点。从模板列表中选择“顺序工作流库”。在“名称”字段中输入“TruckFlow”,最后点击“确定”。
创建了工作流项目后,我们现在就能使用wca.exe工具去生成我们需要在工作流和宿主应用程序之间进行通信所需的自定义活动,反之亦然。我们打算遵循我们在第8章“创建通信活动”中使用过的相同的方法步骤。
创建自定义数据交换活动
1.在你开始创建之前,你需要确认你没有跳过上一节的第9步“编译相关数据服务”这一过程。当wca.exe工具执行后,它需要一个已经编译好的程序集。
2.点击“开始”按钮,然后点击“运行”菜单打开“运行”对话框。
3.“运行”对话框打开后,输入“cmd”,然后点击“确定”,这将进入Windows的命令提示符。
4.更改当前目录以便你能直接访问到你先前创建的TruckService程序集。通常,你输入的命令和下面的差不多:
cd "Chapter17TruckTrackerTruckServicebinDebug"
但是,你指定的目录根据你的实际情况可能会有所不同。
5.接下来通过在命令提示符中输入下面的文本来执行wca.exe工具(注意要包括双引号):
"C:Program FilesMicrosoft SDKsWindowsv6.0ABinWca.exe" TruckService.dll /n:TruckFlow
然后按下回车键。该工具的输出结果应该和下面的相似:
6.wca.exe工具会为你生成两个文件,你需要对它们进行重新命名并移动到工作流目录下。因此,在命令提示符中输入ren ITruckService.Invokes.cs ExternalEventActivities.cs,然后按下回车键。这个文件包含了所生成的CallExternalEvent活动。
7.因为我们刚才重命名的文件是一个工作流活动,因此我们需要把它从当前目录下移到TruckFlow目录中以便进行编译并使用。在命令提示符中输入move ExternalEventActivities.cs ......TruckFlow并按下回车键。
8.对于外部事件活动来说,我们现在仍将使用相同的方式。在命令提示符中输入ren ITruckService.Sinks.cs ExternalEventHandlers.cs,然后按下回车键对文件进行重命名。这个文件包含了所生成的CallExternalEvent活动。
9.为了对文件进行移动,我们需要在命令提示符中输入move ExternalEventHandlers.cs ......TruckFlow并按下回车键。
10.外部数据交换活动现在都创建好了。在这最后一步中,我们要把它们都添加到该工作流项目中。在解决方案资源管理器中右键点击TruckFlow项目,然后选择“添加”,选中“现有项”。当弹出“添加现有项”对话框后,从列表中选择这两个外部事件活动并点击“添加”。
简要地回顾一下,你创建了一个接口,它包含了工作流和应用程序将用来进行信息通信的方法和事件。该接口使用了关联特性来进行修饰,因此每一个方法和事件必须以某种方式来传送关联参数。然后你创建了你将用来在宿主和工作流之间进行信息通信的本地通信服务。最后,你运行了wca.exe工具来生成你可用在你的工作流中去执行数据通信的自定义活动。现在是该创建工作流本身的时候了。
完成相关工作流(correlated workflow)
1.外部数据通信活动现在已是你工作流项目的一部分了,接下来你需要做的第一件事是添加对于通信服务项目的项目级引用。在解决方案资源管理器中右键点击TruckFlow项目,然后选择“添加引用”。在“项目”选项卡的列表中选中TruckService并点击“确定”。
2.编译该工作流项目(注意不是整个解决方案),以便把这些自定义活动加载进Visual Studio的工具箱中方便在工作流视图设计器中使用。
3.确保工作流视图设计器界面已被激活。
4.要放入你的工作流中的第一个活动是ReadyTruck活动。因此从Visual Studio的工具箱中把它拖拽进你的工作流中。
5.你需要为这个活动设置几个属性,第一个是和相关令牌(correlation token)有关的信息。在属性面板中的CorrelationToken属性中输入TruckIDCorrelation,然后按下回车键。
6.在属性窗口中点击“加号”(+),然后CorrelationToken属性将展开OwnerActivityName属性。点击下拉列表框的箭头,从中选中Workflow1(对于本范例应用程序来说仅有这一个选项)。
7.你需要绑定一些数据属性,首先从startingX属性开始。在属性面板中选中startingX属性,然后点击浏览(...)按钮,这将弹出“将‘startingX’属性绑定到活动的属性”对话框。选择“绑定到新成员”选项卡,在“新成员名称”字段中输入CurrentX。最后点击“确定”。
8.对于startingY属性所做的工作也一样。点击startingY属性,然后点击浏览(...)按钮,打开“将‘startingY’绑定到活动的属性”对话框。选择“绑定到新成员”选项卡,在“新成员名称”字段中输入CurrentY。最后点击“确定”。
9.最后,再次在属性面板中选择truckID属性对其进行绑定。点击浏览(...)按钮打开“将‘truckID’绑定到活动的属性”对话框。选择“绑定到新成员”选项卡,在“新成员名称”字段中输入TruckID,然后点击“确定”。
10.回到工作流视图设计器,拖拽一个While活动到设计器界面上,把它放到你刚才放入的readyTruck1活动的下面。
11.你需要添加一个条件表达式,因此选中Condition属性并从列表中选择“代码条件”。展开“条件”属性后面的加号(+),在第二个“条件”属性编辑框中输入TestAtDestination然后按下回车键。Visual Studio就插入了TestAtDestination方法并自动为你切换到代码视图界面下。我们需要返回到工作流视图设计器界面上来。
12.拖拽一个Listen活动到工作流视图设计器界面上,把它放到whileActivity1内部。
13.你刚才添加的这个Listen活动执行两个功能。其中你将在这里开始第一个功能的完成工作。从工具箱中拖拽一个CancelTruck并把它放进左边的EventDriven活动(即eventDrivenActivity1)中。
14.你需要为cancelTruck1创建相关令牌。为此,简单地点击cancelTruck1的CorrelatonToken属性的下拉箭头然后选择TruckIDCorrelation选项。假如箭头没有显示,可选中CorrelationToken属性来去激活它。
15.cancelTruck1需要有已经创建的卡车标识符,因此在truckID属性中点击浏览(...)按钮。假如浏览(...)按钮不存在的话,可再次点击该属性来激活它,就像你刚才可能在相关令牌属性上做过的一样。在“将‘truckID’绑定到活动的属性”对话框打开后,从现有属性列表中选择TruckID,然后点击“确定”。
16.为了在处理CancelTruck事件后执行一些处理过程,你需要拖拽一个Code活动到设计器界面上,把它放到你刚刚添加的cancelTruck1活动的下面。
17.在codeActivity1的ExecuteCode属性中输入CancelTruck,然后按下回车键。在Visual Studio为你添加了CancelTruck方法后回到工作流视图设计器界面上来。
18.回到工作流视图设计器界面后,拖拽一个Delay活动到右边的EventDriven活动(eventDrivenActivity2)中。这就是Listen活动的第二个功能,它执行了GPS卡车定位扫描的模拟任务。
19.设置delayActivity1的TimeoutDuration为1秒。这代表了你的工作流将用来对用户界面进行更新的刷新频率。
20.拖拽一个Code活动到设计器界面上,把它放到你刚刚放入的Delay延时活动的下面。把它的名称更改为updatePosition,在它的ExecuteCode属性中输入UpdateTruckPosition并按下回车键。
21.回到工作流视图设计器界面上来。该updatePosition Code活动执行了卡车位置测定的模拟任务,其结果就是需要把视觉处理结果传给宿主应用程序。为了把结果传给宿主应用程序,需要拖拽一个UpdateTruck活动到设计器界面上并把它放到updatePosition活动的下面。
22.通过点击updateTruck1属性的下拉箭头,在其选择列表中选择CorrelationToken属性。当单击鼠标激活下拉箭头后你可能只有一个CorrelationToken属性供你选择。
23.在激活浏览(...)按钮后点击truckID属性以便你能为updateTruck1活动指定关联的卡车标识符。点击该按钮,在“将‘truckID’绑定到活动的属性”对话框中的现有属性列表中选择TruckID,然后点击“确定”。
24.为了设置updateTruck1的X属性,需要再次点击浏览(...)按钮把X绑定到已存在的CurrentX属性。对于Y属性也做同样的工作,把它绑定到CurrentY属性。
25.在你取消对一辆卡车的模拟或者卡车抵达了它的终点之前,模拟过程会一直运行。前面的任何一个条件都会导致whileActivity1终止它的循环。在此时,用户界面需要把该卡车移除。因此,拖拽一个RemoveTruck活动到工作流视图设计器界面上,并把它放到whileActivity1的下面。
9.保存该文件并编译该TruckService项目。如果存在编译错误,请纠正任何出现的编译错误。
服务类的代码就全部完成了,列表17-3展示了该类的完整代码。TruckSercie本地通信服务可以准备使用了。我们还没有一个工作流来使用该服务。我们也需要使用可信赖的wca.exe工具来为我们创建自定义的CallExternalMethod和HandleExternalEvent活动。
列表17-3 WorkflowTruckTrackingDataService.cs的完整代码
WorkflowTruckTrackingDataService类的完整代码
using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.Activities;
using System.Workflow.Runtime;
namespace TruckService
{
public class WorkflowTruckTrackingDataService
{
private static WorkflowRuntime _workflowRuntime = null;
private static ExternalDataExchangeService _dataExchangeService = null;
private static TruckServiceDataConnector _dataConnector = null;
private static object _syncRoot = new object();
public event EventHandler<TruckActivityEventArgs> TruckLeaving;
public event EventHandler<TruckActivityEventArgs> RouteUpdated;
public event EventHandler<TruckActivityEventArgs> TruckArrived;
private Guid _instanceID = Guid.Empty;
public Guid InstanceID
{
get { return _instanceID; }
set { _instanceID = value; }
}
private Int32 _truckID = -1;
public Int32 TruckID
{
get { return _truckID; }
set { _truckID = value; }
}
public static WorkflowTruckTrackingDataService CreateDataService(Guid instanceID, WorkflowRuntime workflowRuntime, Int32 truckID)
{
lock (_syncRoot)
{
// If we're just starting, save a copy of the workflow runtime reference
if (_workflowRuntime == null)
{
_workflowRuntime = workflowRuntime;
} // if
// If we're just starting, plug in ExternalDataExchange service
if (_dataExchangeService == null)
{
_dataExchangeService = new ExternalDataExchangeService();
_workflowRuntime.AddService(_dataExchangeService);
} // if
// Check to see if we have already added this data exchange service
TruckServiceDataConnector dataConnector = (TruckServiceDataConnector)workflowRuntime.GetService(typeof(TruckServiceDataConnector));
if (dataConnector == null)
{
_dataConnector = new TruckServiceDataConnector();
_dataExchangeService.AddService(_dataConnector);
} // if
else
{
_dataConnector = dataConnector;
} // else
// Pull the service instance we registered with the connection object
return WorkflowTruckTrackingDataService.GetRegisteredWorkflowDataService(instanceID, truckID);
} // lock
}
public static WorkflowTruckTrackingDataService GetRegisteredWorkflowDataService(Guid instanceID, Int32 truckID)
{
lock (_syncRoot)
{
WorkflowTruckTrackingDataService workflowDataService = TruckServiceDataConnector.GetRegisteredWorkflowDataService(instanceID, truckID);
if (workflowDataService == null)
{
workflowDataService = new WorkflowTruckTrackingDataService(instanceID, truckID);
TruckServiceDataConnector.RegisterDataService(workflowDataService);
} // if
return workflowDataService;
} // lock
}
private WorkflowTruckTrackingDataService(Guid instanceID, Int32 truckID)
{
this._instanceID = instanceID;
this._truckID = truckID;
}
~WorkflowTruckTrackingDataService()
{
// Clean up
_workflowRuntime = null;
_dataExchangeService = null;
_dataConnector = null;
}
public string Read()
{
return _dataConnector.RetrieveTruckInfo(InstanceID, TruckID);
}
public void RaiseTruckLeavingEvent(Int32 truckID, Int32 startingX, Int32 startingY)
{
if (_workflowRuntime == null)
_workflowRuntime = new WorkflowRuntime();
_workflowRuntime.GetWorkflow(_instanceID); // loads persisted workflow instances
if (TruckLeaving != null)
{
TruckLeaving(this, new TruckActivityEventArgs(_instanceID, truckID, startingX, startingY));
} // if
}
public void RaiseRouteUpdatedEvent(Int32 truckID, Int32 X, Int32 Y)
{
if (_workflowRuntime == null)
_workflowRuntime = new WorkflowRuntime();
_workflowRuntime.GetWorkflow(_instanceID); // loads persisted workflow instances
if (RouteUpdated != null)
{
RouteUpdated(this, new TruckActivityEventArgs(_instanceID, truckID, X, Y));
} // if
}
public void RaiseTruckArrivedEvent(Int32 truckID)
{
if (_workflowRuntime == null)
_workflowRuntime = new WorkflowRuntime();
_workflowRuntime.GetWorkflow(_instanceID); // loads persisted workflow instances
if (TruckArrived != null)
{
TruckArrived(this, new TruckActivityEventArgs(_instanceID, truckID));
} // if
}
}
}