Windows Workflow Foundation (Basics): How to handle external events and to trigger state transitions using a HandleExternalEvent activity

Windows Workflow Foundation (Basics): How to handle external events and to trigger state transitions using a HandleExternalEvent activity

  • Comments 9

 

MSDN's description for a HandleExternalEvent reads as "Defines a workflow communication activity that is used to handle an event that is raised by a local service". In this blog I will try to expand on that definition.

Imagine a simple state machine representing a moving (state = Moving) or non-moving (state = Stopped) vehicle. For simplicity, let's assume that the vehicle can only start moving if the driver accelerates and it stops when the brakes are applied:

WF's state machine runtime allows services to raise events which can be handled by the state machine. In other words, a local service could perhaps communicate with a state machine via this route.

Let’s create a simple service project which will allow us to raise both the accelerate and brake events:

- Create an empty class library called VehicleService
- Inherit from ExternalDataEventArgs and mark the inherited class as serializable (it must be marked as serializable in order to be sent to the state machine):

[Serializable]
public class MovementChangeEventArgs : ExternalDataEventArgs 
{
   private MovementChangeType _movementChangeType;
  
public MovementChangeType MovementChangeType
  
{
      get { return _movementChangeType; }
      set { _movementChangeType = value; }
   }

 

   public MovementChangeEventArgs(Guid instanceId, MovementChangeType
                movementChangeType) : base(instanceId)
   {
      _movementChangeType = movementChangeType;
      this.WaitForIdle = true;
   }
}

public enum MovementChangeType
{
   Brake,
   Accelerate
}

- Create an interface which can be shared by both the local service (the service which has the responsibility for raising the event) and the state machine.
- Add only a single event to that interface

[ExternalDataExchange]
public interface IVehicleService
{
  
event EventHandler<MovementChangeEventArgs> MovementChanged;
}

- As you can see above, the interface is marked with the ExternalDataExchangeAttribute attribute which flags the interface as a local service used for data exchange purposes.
- The next step is to implement the IVehicleService interface:

public class VehicleService : IVehicleService
{
  
public void RaiseEvent(MovementChangeEventArgs args)
  
{
     
EventHandler<MovementChangeEventArgs> movementChanged = MovementChanged;
      if (movementChanged != null)
     
{
        
movementChanged(null, args);
     
}
  
}
  
public event EventHandler<MovementChangeEventArgs>MovementChanged;
}

This interface now can be registered with the state machine. For this, you will need a HandleExternalEvent activity as the first event driven activity within the workflow of both the stopped and moving states:

- VehicleService class library must be referenced by the workflow project
- Go to the stopped state and drop a HandleExternalEvent activity as the first item within the event driven workflow. You will also need to set the following properties on this activity:
   o InterfaceType: IVehicleService
   o EventName: MovementChanged
- Now attach an event handler to the Invoked event of this activity which is called when the MovementChanged event is invoked. At this stage, you have a number of options open to you. One option is to keep the result of the event using a local property. You could perhaps add an IfThenElse activity followed by a SetState to your workflow which could trigger a state transition based on that local property.

The very last step is to make the workflow runtime aware of the service:

WorkflowRuntime workflowRuntime = new WorkflowRuntime();
ExternalDataExchangeService dataExchange = new ExternalDataExchangeService();
workflowRuntime.AddService(dataExchange);
VehicleService vehicleService = new VehicleService();
dataExchange.AddService(vehicleService);

You can now simply raise events by calling the RaiseEvent method of the vehicleService instance:

vehicleService.RaiseEvent(
   new MovementChangeEventArgs(instance.InstanceId,
      
MovementChangeType.Brake));

Please let me know what you think about this blog entry.

Leave a Comment
  • Please add 2 and 7 and type the answer here:
  • Post
  • I have gone through the beautiful explanation .One clarification I want is that you have told to "make the workflow runtime aware of the service”. You have also showed the code .But my question is where to write this code ?

    It would be helpful if you answer this query.

  • Vikas, you need a workflow runtime to run your workflow for you. The service needs to be added to the runtime and it is usually done at the same time as you create the runtime. The runtime can be created anywhere in the host application. For instance it can be included in the Main method of a console app.

  • Hi Pedramr,

     Thank you very much for responding to my query.Now I am having a different problem.

    As per your explanation, Now I am using a class library and a service  for some workflow application(Bug tracker).This class library should be placed in the GAC of sharepoint server.

    So it is clear that it is not an console application.

    Now where to write the code for "making the workflow runtime aware of my service"

  • where would i put this & please explain the  instance.InstanceId . Do I keep the vehicleService in a session from b4 .... come on finish it off now u have done a really good article!!

    vehicleService.RaiseEvent(

      new MovementChangeEventArgs(instance.InstanceId,

          MovementChangeType.Brake));

  • Hi

    If the workflow has a long life, i.e. the events are as a result of users going off and doing something, and the event can cause a state transition, i'm concerned that a server reboot in the mean time can cause all state to be lost for all instances.

    Is it normal to persist (dehydrate) the workflow instance after each event and reload it (rehydrate) before each event is raised. Or does this cause a big performance overhead?

    Ta

  • hi,

    I have created the sample state machine workflow in asp.net in VB code, with two states. the first state has state initialization and event driven activity, and the second state has state initialization. when start the workflow, it works fine at the initial state. when i raise a event using the handle external event activity it doesn't move to next state.

    I know that i have made a mistake, but couldn't able to trace. could you help me.

    Thanks in advance

    Venkatesh

  • hi,

    I followed the instructions given above but, in asp.net by vb code, it could able move to the next state.

    any one can help me

    thanks in advance

    Venkatesh

  • hi,

    I have created the sample state machine workflow in asp.net, with three states. the first state include handle external activity,code activity and call external activity, and the second state has handle external event. when start the workflow, it works fine at the initial state. when i raise a event using the handle external event activity in second state,the event has the value null.any one can help me

    Thanks in advance

  • hi,

    I have created the  state machine workflow , the second state has handle external event. when start the workflow, it works fine at the initial state. when i raise a event using the handle external event activity in second state,the event has the value null

    Thanks in advance

Page 1 of 1 (9 items)