事件驱动活动开发全解析
立即解锁
发布时间: 2025-08-26 01:37:08 阅读量: 7 订阅数: 19 


Windows Workflow in .NET 3.5: 实战指南
### 事件驱动活动开发全解析
#### 1. 事件驱动活动基础
在不同的机器上执行事件序列时,由于机器速度和配置的差异,事件顺序可能会稍有不同,但基本的事件集应该与预期结果非常相似。从执行结果可以明显看出,主线活动以每秒一次的速率执行。经过短暂延迟后,宿主应用程序开始引发事件,包括同一事件(如EventOne)的多次触发。最终,当EventStop事件被引发时,主线活动和工作流完成。
EventHandlingScopeActivity是一个强大但复杂的活动,适用于需要以正常方式执行一组主线活动,同时又要并发响应一个或多个事件的场景。
#### 2. 开发自定义事件驱动活动
通常,我们可以使用WF提供的标准事件驱动活动来处理工作流应用程序中的大多数常规事件处理需求。但当这些标准活动无法满足需求时,我们可以开发自己的自定义事件驱动活动。
以下是开发自定义事件驱动活动的步骤概述:
1. 了解WF队列机制
2. 以猜数字游戏为例,开发一组自定义事件驱动活动
3. 用新的自定义活动替换原示例中使用的标准活动(CallExternalMethodActivity、HandleExternalEventActivity)
需要注意的是,这里展示的是事件驱动活动的高级用法。即使不使用这些技术,也能开发出各种工作流应用程序。实际上,原猜数字游戏示例的版本更简单,是更好的实现方式。这里重新探讨该示例,只是为了对比两种实现方式的差异。
#### 3. 理解工作流队列
WF运行时提供了一个内部队列机制,用于一般的流程调度和通信。消息被加入到一个命名队列中,然后由工作流实例稍后取出。入队的消息可以是任何可序列化的.NET类型。
标准的事件驱动活动和ExternalDataExchangeService在内部使用工作流队列。例如,当从本地服务引发一个事件时,该事件会被加入到工作流队列中。当HandleExternalEventActivity接收到队列中有新消息可用的通知时,事件会被出队并处理。在实现自定义事件驱动活动时,也可以类似地使用队列。
在工作流或自定义活动中,WorkflowQueue类表示一个命名队列。该类实现了Enqueue、Peek和Dequeue等方法,使工作流或活动能够与队列中的消息进行交互。WorkflowQueue还支持事件,当有新消息可供处理(QueueItemAvailable)或入队的消息已送达(QueueItemArrived)时会发出通知。
工作流或活动使用WorkflowQueuingService来获取WorkflowQueue对象的实例。这是一个内置的工作流服务,由工作流运行时自动加载。与许多其他工作流服务不同,我们不能将此服务的实现替换为自己的实现。在工作流或活动中,可以通过调用ActivityExecutionContext的GetService方法来获取对这个内置服务的引用。WorkflowQueuingService提供了创建新队列(CreateWorkflowQueue)、检索现有队列(GetWorkflowQueue)或删除队列(DeleteWorkflowQueue)的方法。
工作流队列仅允许从宿主应用程序或本地服务向工作流实例发送消息,不支持从工作流实例向宿主应用程序或本地服务发送消息。因此,WorkflowQueue和WorkflowQueuingService只能在工作流或自定义活动中使用。
宿主应用程序或本地服务可以使用WorkflowInstance类的EnqueueItem或EnqueueItemOnIdle方法将消息入队供工作流实例使用。EnqueueItem会立即添加新消息,而EnqueueItemOnIdle会等待工作流实例处于空闲状态后再入队消息。由于通信方向总是从宿主应用程序到工作流实例,所以WorkflowInstance类没有出队消息的方法。宿主应用程序可以使用WorkflowInstance类的GetWorkflowQueueData方法获取工作流队列的信息,该方法返回一个包含工作流实例使用的所有队列信息的WorkflowQueueInfo对象集合。
以下是工作流队列相关操作的总结表格:
| 操作 | 方法 | 说明 |
| ---- | ---- | ---- |
| 入队消息 | EnqueueItem、EnqueueItemOnIdle | EnqueueItem立即添加,EnqueueItemOnIdle等待空闲 |
| 出队消息 | 无(工作流实例处理) | 工作流实例从队列中取出消息 |
| 获取队列信息 | GetWorkflowQueueData | 返回包含队列信息的集合 |
#### 4. 事件驱动活动的要求
事件驱动活动有其特殊性,它不会通过调用Execute事件立即开始执行,而是注册对一个用于触发其执行的事件的兴趣。注册事件后,活动会等待该触发事件的到来。
IEventActivity接口定义了事件驱动活动必须实现的契约。该接口定义了Subscribe和Unsubscribe方法,用于事件注册过程,这些方法由活动的父级调用。Subscribe和Unsubscribe方法传递一个实现了IActivityEventListener接口的对象。IEventActivity还定义了一个QueueName属性,用于返回活动用于接收消息的队列名称。
IEventActivity接口没有定义实际接收事件通知的方式,这由IActivityEventListener接口完成,事件驱动活动也应该实现该接口。IActivityEventListener定义了一个名为OnEvent的方法,当接收到新事件并可进行处理时会调用该方法。IActivityEventListener是一个泛型接口,需要指定要处理的事件参数的类型。在开发基于队列的事件驱动活动时,事件参数的类型总是QueueEventArgs。
传统上,事件驱动活动用作EventDrivenActivity(或ListenActivity和EventDrivenActivity的组合)的子活动。但自定义事件驱动活动也可以直接在工作流中使用,而不依赖于EventDrivenActivity。活动内的处理和方法调用顺序会根据使用方式略有不同,建议开发的事件驱动活动能在两种情况下都能正常工作。
以下是事件驱动活动在不同使用场景下的处理流程:
- **作为EventDrivenActivity的子活动**:
1. 执行Initialize方法,进行活动的初始化任务。
2. 执行Subscribe方法,创建命名工作流队列(如果不存在),并让父活动订阅队列的QueueItemAvailable事件。
3. 当队列中有新消息时,订阅事件的父活动会收到通知。
4. 执行Unsubscribe方法,从父活动中移除事件订阅。
5. 执行Execute方法,从队列中取出新消息并处理,最后返回ActivityExecutionStatus.Closed表示处理完成。
```mermaid
graph LR
A[Initialize] --> B[Subscribe]
B --> C{新消息到达?}
C -- 是 --> D[Unsubscribe]
D --> E[Execute]
E --> F[返回Closed]
C -- 否 --> C
```
- **直接在工作流中使用**:
1. 执行Initialize方法,初始化活动状态。
2. 执行Execute方法,创建命名工作流队列(如果不存在),并让自己订阅QueueItemAvailable事件,返回ActivityExecutionStatus.Executing表示活动未完成工作。
3. 当队列中有新消息时,执行OnEvent方法,从队列中取出新消息并处理,取消队列事件通知并关闭活动。
```mermaid
graph LR
A[Initialize] --> B[Execute]
B --> C[返回Executing]
C --> D{新消息到达?}
D -- 是 --> E[OnEvent]
E --> F[处理消息]
F --> G[取消通知]
G --> H[关闭活动]
D -- 否 --> D
```
#### 5. 实现ProcessGuessActivity
ProcessGuessActivity的目的是处理从宿主应用程序传递过来的猜测。以下是具体的实现步骤:
1. 在SharedWorkflows项目中添加一个新活动,命名为ProcessGuessActivity。
2. 将基类改为Activity,因为它不是一个复合活动。
以下是ProcessGuessActivity.cs文件的完整代码:
```csharp
using System;
using System.ComponentModel;
using System.Workflow.ComponentModel;
using System.Workflow.Runtime;
using System.Workflow.Activities;
namespace SharedWorkflows
{
/// <summary>
/// A custom event-driven activity that accepts
/// the guessing game input via a workflow queue
/// </summary>
public partial class ProcessGuessActivity : Activity, IEventActivity,
IActivityEventListener<QueueEventArgs>
{
private readonly String _queueName = "GuessQueue";
#region Dependency Properties
public static DependencyProperty TheNumberProperty
= System.Workflow.ComponentModel.DependencyProperty.Register(
"TheNumber", typeof(Int32), typeof(ProcessGuessActivity));
[Description("The number to guess")]
[Category("Event Handling")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Int32 TheNumber
{
get
{
return ((Int32)(base.GetValue(
ProcessGuessActivity.TheNumberProperty)));
}
set
{
base.SetValue(ProcessGuessActivity.TheNumberProperty, value);
}
}
public static DependencyProperty MessageProperty
= System.Workflow.ComponentModel.DependencyProperty.Register(
"Message", typeof(String), typeof(ProcessGuessActivity));
[Description("A hint message")]
[Category("Event Handling")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public String Message
{
get
{
return ((String)(base.GetValue(
ProcessGuessActivity.MessageProperty)));
}
set
{
base.SetValue(ProcessGuessActivity.MessageProperty, value);
}
}
public static DependencyProperty IsCompleteProperty
= System.Workflow.ComponentModel.DependencyProperty.Register(
"IsComplete", typeof(Boolean), typeof(ProcessGuessActivity));
[Description("Signal that the correct guess was received")]
[Category("Event Handling")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVi
```
0
0
复制全文
相关推荐









