而对于本地的通讯,我们有CallExternalMethod activity 和 HandleExternalEvent activity。前者允许工作流来调用宿主中注册的程序,而后者是让工作流通过宿主来监听对应的事件。
那么,事件是怎么传入工作流的呢,很明显,是宿主来告诉工作流哪些事件在什么时候发生的。事件在工作流中可以存在一段很长的路径,这一点不同于winform和web程序中的事件(直接从响应者到发起者),工作流实例存在于内存当中,我们得按照一定的规则来唤起工作流和运行,并且这只是一部分,因为工作流可能要等待很长一段时间事件才能到达,很可能工作流这时已被持久化到数据库中去了,工作流不可能在内存中等n个小时甚至更长的时候来等待事件的到达。
这里所说的规则就是:首先你得定义好事件和相关的参数类型。
[ExternalDataExchange]
public interface IPaymentProcessingService
{
event EventHandler PaymentProcessed;
}
[Serializable]
public class PaymentProcessedEventArgs : ExternalDataEventArgs
{
public PaymentProcessedEventArgs(Guid instanceId, double amount)
: base(instanceId)
{
_amount = amount;
}
private double _amount;
public double Amount
{ get { return _amount; }
set { _amount = value; }
}
}
这里有三点要注意:
(a)我们必须打上ExternalDataExchange标签,在这里,我们不能不提一上数据交换服务:
什么叫数据交换服务[DataExchangeService]工作流通过专门为此目的建立的服务与外部世界通信。该服务会引发工作流内的事件驱动活动将挂钩到的事件。同样,该服务公开了供该工作流调用的公共方法并向主机发送数据。方法和事件在接口中定义。该接口也称为数据交换服务。每当工作流与外部组件交互(进行输入和输出)时,您都需要该服务
数据交换服务是常规的 .NET 类库,它最起码包含一个接口定义以及一个实现该接口的类。该
接口是为您希望表示的任务定制的。
在本例中,[ExternalDataExchange] 属性将 IPaymentProcessingService标记为数据交换服务接口,以便工作流运行库知道它将用来与工作流实例交换数据。在此情况下,宿主将向一串 EventDriven 活动引发事件,从而向工作流实例发送数据
(b)事件中参数类型必须继承自ExternalDataEventArgs类
©参数类型必须打上Serializable标签,表示能被序列化。如果没打上此标签,你会发现一些莫明其妙的错误,如:“Event PaymentProcessed on interface type IPaymentProcessingService for instance ID [GUID] cannot be delivered”,这个异常就是因为没有序列化参数的原因。
当我们在Wf中注册这个服务(IPaymentProcessingService)时,运行库将根据ExternalDataExchange标签来找到相应服务接口,当然没找到的时候就会抛出“Service does not implement an interface with the ExternalDataExchange attribute”异常,当运行库找到这个属性标签时,它就会为这些事件生成监听代理类。这些代理类能捕获事件并将他们发送到相应的工作流实列中,当然如果这个工作流长时间休眠在数据库中,他也将被唤醒。
注意到ExternalDataEventArgs类中需要一个guid类型的参数,这个参数就是我们想让事件注册到的工作流实例的标识,activites会验证这些参数是否设置正确。
当我们把
handleExternalEvent activity拖入到设计器中,我们必须指定这个activity监听的接口和事件,如果我们没有正确按照上面的要求定义好DataExchangeService,那么当你指定的时候会抛出“
The event PaymentProcessed has to be of type EventHandler where T derives from ExternalDataEventArgs”这样的异常,一旦抛出了,你就得好好检查你的DataExchangeService申明是否注意到了我上面所说的那些东西。
对于实现IPaymentProcessingService的类可以如下:
class PaymentProcessingService : IPaymentProcessingService
{
public void ProcessPayment(Guid id, double amount)
{
// ... do some work work work
// ... then raise an event to let everyone know
PaymentProcessedEventArgs args;
args = new PaymentProcessedEventArgs(id, amount);
EventHandler evh;
evh = PaymentProcessed;
if (evh != null)
evh(this, args); // boom!
}
public event EventHandler
PaymentProcessed;
}
工作流实例实际上不需要触发一些服务(如果需要调用的话,你可以使用CallExternalEvent activity),所以我们把Sender参数设为null.
if (evh != null)
evh(null, args); // boom!
}
最后我们必参在工作流运行时上配置好这个服务,ExternalDataExchangeService 管理着宿主中本地的服务,所以我们得加上:
WorkflowRuntime workflowRuntime = new WorkflowRuntime();
ExternalDataExchangeService dataExchangeService;
dataExchangeService = new ExternalDataExchangeService();
workflowRuntime.AddService(dataExchangeService);
PaymentProcessingService paymentProcessing;
paymentProcessing = new PaymentProcessingService();
dataExchangeService.AddService(paymentProcessing);
//not workflowRuntime.AddService(paymentProcessing);
这里要注意的是,我们不是在workflowRuntime中注册ExternalDataExchangeService之类服务的。这是我们容易出错的地方