English 中文(简体)
Statefinalization/initialization activity only runs on leaf states
原标题:

I am trying to get my Windows State Machine workflow to communicate with end users. The general pattern I am trying to implement within a StateActivity is:

StateInitializationActivity: Send a message to user requesting an answer to a question (e.g. "Do you approve this document?"), together with the context for...
...EventDrivenActivity: Deal with answer sent by user
StateFinalizationActivity: Cancel message (e.g. document is withdrawn and no longer needs approval)

This all works fine if the StateActivity is a "Leaf State" (i.e. has no child states). However, it does not work if I want to use recursive composition of states. For non-leaf states, StateInitialization and StateFinalization do not run (I confirmed this behaviour by using Reflector to inspect the StateActivity source code). The EventDrivenActivity is still listening, but the end user doesn t know what s going on.

For StateInitialization, I thought that one way to work around this would be to replace it with an EventDrivenActivity and a zero-delay timer. I m stuck with what to do about StateFinalization.

So - does anyone have any ideas about how to get a State Finalization Activity to always run, even for non-leaf states?

问题回答

Its unfortunate that the structure of "nested states" is one of a "parent" containing "children", the designer UI re-enforces this concept. Hence its quite natural and intuative to think the way you are thinking. Its unfortunate because its wrong.

The true relationship is one of "General" -> "Specific". Its in effect a hierachical class structure. Consider a much more familar such relationship:-

public class MySuperClass
{
    public MySuperClass(object parameter) { }
    protected void DoSomething() { }
}

public class MySubClass : MySuperClass
{
    protected void DoSomethingElse() { }
}

Here MySubClass inherits DoSomething from SuperClass. The above though is broken because the SuperClass doesn t have a default constructor. Also parameterised constructor of SuperClass is not inherited by SubClass. In fact logically a sub-class never inherits the constructors (or destructors) of the super-class. (Yes there is some magic wiring up default constructors but thats more sugar than substance).

Similarly the relationship between StateAcivities contained with another StateActivity is actually that the contained activity is a specialisation of the container. Each contained activity inherits the set of event driven activities of the container. However, each contained StateActivity is a first class discrete state in the workflow same as any other state.

The containing activity actual becomes an abstract, it can not be transitioned to and importantly there is no real concept of transition to a state "inside" another state. By extension then there is no concept of leaving such an outer state either. As a result there is no initialization or finalization of the containing StateActivity.

A quirk of the designer allows you to add a StateInitialization and StateFinalization then add StateActivities to a state. If you try it the other way round the designer won t let you because it knows the Initialization and Finalization will never be run.

I realise this doesn t actually answer your question and I m loath to say in this case "It can t be done" but if it can it will be a little hacky.

OK, so here’s what I decided to do in the end. I created a custom tracking service which looks for activity events corresponding to entering or leaving the states which are involved in communication with end users. This service enters decisions for the user into a database when the state is entered and removes them when the state is left. The user can query the database to see what decisions the workflow is waiting on. The workflow listens for user responses using a ReceiveActivity in an EventDrivenActivity. This also works for decisions in parent ‘superstates’. This might not be exactly what a "Tracking Service" is meant to be for, but it seems to work

I ve thought of another way of solving the problem. Originally, I had in mind that for communications I would use the WCF-integrated SendActivity and ReceiveActivity provided in WF 3.5.

However, in the end I came to the conclusion that it s easier to ignore these activities and implement your own IEventActivity with a local service. IEventActivity.Subscribe can be used to indicate to users that there is a question for them to answer and IEventActivity.Unsubscribe can be used to cancel the question. This means that separate activities in the State s inialization and finalization blocks are not required. The message routing is done manually using workflow queues and the user s response is added to the queue with appropriate name. I used Guid s for the queue names, and these are passed to the user during the IEventActivity.Subscribe call.

I used the File System Watcher example in MSDN to work out how to do this. I also found this article very insructive: http://www.infoq.com/articles/lublinksy-workqueue-mgr





相关问题
Recursive same-table query in SQL Server 2008

I have the following table in a SQL Server 2008 database: Id Name ParentFolder -- ---- ------------ 1 Europe NULL 2 Asia NULL 3 Germany 1 4 UK 1 5 China ...

Finding a class within list

I have a class (Node) which has a property of SubNodes which is a List of the Node class I have a list of Nodes (of which each Node may or may not have a list of SubNodes within itself) I need to be ...

Selecting records during recursive stored procedure

I ve got a content management system that contains a hierarchical structure of categories, with sub-categories subject to different ordering options at each level. Currently, that s retrieved by a (...

热门标签