HandleExternalEvent: event filter / correlation

Hello folks,
I am new to WF and I am completely baffled by correlation. What I am trying to do is very simple: I have a State workflow activated by a single event from my local service with a parameter in the event args to do the disambiguation. The HandleExternalEvent activities knows those values at design time, but not at compile time.

What I wanted to do initially, and what is my preferred solution, is to derive a new class from HandleExternalEventActivity, add a design-time property that will match the eventarg and filter the events for events matching the property. The problem with this approach is that HandleExternalEventActivity's virtual methods are sealed ; I didn't find a way to make this approach work. (Ideally HandleExternalEventActivity would have a virtual method IsEventValid which would return true in the base class and where I could match the property to the eventarg value.) Is this approach possible

Then I tried to use correlation. The only way I was able to make correlation work is by including a CallExternalMethod activity for every HandleExternalEvent in a new initial state's StateInitializationActivity (because correlation can only be initialized once and a state can be active multiple times). It works, but I really dislike this solution because my state machine is very large, leading to a very large number of CallExternalMethod (error prone, not easily maintanable, inefficient). Is there a way to subclass HandleExternalEventActivity and simulate a CallExternalMethod

I also tried a few other things like creating a composite activity that called both CallExternalMethod and HandleExternalEvent while implementing IEventActivity, but my workflow didn't compile when I used the composite activity in the workflow. I could also use reflection to create a LocalService dynamically, but that is alot of trouble for a simple event filter.

What's the better way to do this
Thanks for your help!
Charles


Answer this question

HandleExternalEvent: event filter / correlation

  • rafaelc

    Hi,

    I had to implement the IEventActivity and IActivityEventListener<QueueEventArgs> interfaces manually. I wouldn't be able to do it myself but fortunately there was a sample that did exactly that in the doc. I don't remember where the sample is...

    Good luck,

    Charles


  • stenis

    Hi Tom, thanks for the answer.

    Unfortunately that is not the problem. I am working on a kiosk. The order that the screens are shown is determined by a (complex) state machine workflow. That workflow and the screens can be changed by the clients.

    As far as I know, if I use HandleExternalEvents, every EventDriven in the state must be connected to a different event in the local service. That configuration works great. But in my case, I don't know in advanced how many events are needed in the local service since it's the clients that decide the workflow. My solution is to have a single event with an EventArg containing a parameter which must be matched in the event driven to a value that the client decides.

    For example the clients want to add a button from the main screen to the information screen. In Expression Interactive Designer, he adds my custom button class and sets the property NextScreen to informationScreen.xaml. He then adds an event driven to the mainScreen state, add a HandleExternalEvents on my service for my event ChangeScreen and specifies to only fire if the nextScreen attribute of the event args matches "informationScreen.xaml". He then adds a SetState activity that transitions to InformationScreen.

    As far as I know the only way to do this cleanly is to create a new (custom)HandleExternalEvents that support filtering on the event args. Refer to the first post for various things that I tried.

    Charles


  • Angelvs

    Thanks for the information.
    Here's the code of my attempt to make the custom composite for state workflows:

    [Designer(typeof(ActivityDesigner), typeof(IDesigner))]
    public partial class WaitForKioskEventActivity2: SequenceActivity, IEventActivity
    {
    public WaitForKioskEventActivity2()
    {
    InitializeComponent();
    }

    private string m_sScreenFile;

    [BrowsableAttribute(true)]
    [CategoryAttribute("Behavior")]
    [DescriptionAttribute("Workflow screen change to wait for.")]
    [EditorAttribute(typeof(ScreenSelector), typeof(UITypeEditor))]
    public string ScreenFile
    {
    get { return m_sScreenFile; }
    set {
    m_sScreenFile = value;
    if (this.DesignMode == false)
    {
    callExternalMethodActivity1.CorrelationToken.Properties.Clear();
    callExternalMethodActivity1.CorrelationToken.Properties.Add(new CorrelationProperty("StringFile", m_sScreenFile));
    }
    }
    }

    #region IEventActivity Members

    public IComparable QueueName
    {
    get { return ((IEventActivity)handleExternalEventActivity1).QueueName; }
    }

    public void Subscribe(ActivityExecutionContext parentContext, IActivityEventListener<QueueEventArgs> parentEventHandler)
    {
    ((IEventActivity)handleExternalEventActivity1).Subscribe(parentContext, parentEventHandler);
    }

    public void Unsubscribe(ActivityExecutionContext parentContext, IActivityEventListener<QueueEventArgs> parentEventHandler)
    {
    ((IEventActivity)handleExternalEventActivity1).Unsubscribe(parentContext, parentEventHandler);
    }

    #endregion
    }

    In the designer I obtain some: 'Object reference not set to an instance of an object' message boxes and it fails to compile with the following error:

    Activity 'eventDrivenActivity1' validation failed: The first 'EventDrivenActivity' child should implement 'System.Workflow.Activities.IEventActivity', such as 'HandleExternalEventActivity' or 'DelayActivity'.

    I also use WF b2.2 from winfx b2.

    Charles


  • Sansao Machiana

    >because correlation can only be initialized
    >once and a state can be active multiple times

    That is exactly what I have found out. You could mark your event with [CorrelationInitializer] attribute, vut this does not work well. If you do so, the HandleExternalActivity still listens on the queue which does not include the correlation parameters in its name (because the correlation token has not been initialized yet). As you have probably found out, the HandleExternalEvent.Initialize virtual method is sealed, so you can not initialize the token here.

    I have found 2 sollutions for this problem (and still looking for the third - better one).

    1, Forget the DataExchangeService and implement low-lewel mesage passing through the queues:

    Disadvanteges:
    - you loose the benefits of DataExchangeService (such as automatic dehydration of persisted workflow instance when an event is fired)
    - it takes a lot of work - take a look at FileWatcher SDK sample. You have to manually implement IEventActivity and IActivityListener

    2. Develop a custom composite activity (as you have tried)
    I managed to develop a composite activity with two sub activities:
    - a dummy call to external method which initializes correlation token.
    - a HandleExternalEvent activity

    You should apply [Designer(typeof(ActivityDesigner), typeof(IDesigner))] attribute to your custom to hide those two sub-activites from user who is designing the workflow.
    It works well in sequential workflows. I am using beta 2.2.

    You might want to track the following thread, where I have asked a similar questions http://forums.microsoft.com/MSDN/ShowPost.aspx PostID=476442&SiteID=1

    Bye,
    Matra


  • rsri

    Hi,

    The answer for the error message "The first 'EventDrivenActivity' child should implement 'System.Workflow.Activities.IEventActivity'" has just been posted at http://forums.microsoft.com/MSDN/showpost.aspx postid=488698&siteid=1 - you can not use a composite activity in StatmachineWorkflow, because it does not directly implement IEventActivity:

    >Note that the above CustomDoSomethingActivity would NOT
    >implement IEventActivity and therefore could not be
    >
    used to start an EventDriven and, again, could not be used in StateMachineWorkflow.

    Howerver, I have managed to use such CompositeActivity in SequentialWorkflow, because it deos not have strict rules about event driven activities.

    Was this helpfull

    Bye,
    Matra


  • Simon.kx

    Take a look at the How to send data to a workflow post on my blog. The sample has a single event fired to a state machine workflow and it does not use correlation.

  • SorinD

     Thanks Matra

    I am working on a state machine workflow so unfortunately you confirm that that approach won't work.

    I have been working on another way to do this without correlation but it does not work. I added properties to the state machine workflow and after HandleExternalEvent captures the event, I use a custom rule in an IfElseActivity to do the disambiguation. If the event arg value is appropriate the rule will be true and the state will change to the specified state.

    The problem is that every handle external event has its own queue and there are no mechanism to make the message go to the next queue in case the rule evaluated that the value was not appropriate for the transition.

    Since nobody seems to be answering on the best way to do this in a state machine workflow, I will probably have to code by own HandleExternalEvent and I will make sure not to seal all the interresting methods.

    Charles


  • Vimall

    Do you have a solution now Have the same problem...


  • HandleExternalEvent: event filter / correlation