Search code examples
wcfworkflow-foundation-4workflowserviceworkflow-services

How to force WF4 State Machine workflow service to throw on unexpected message name?


I have a *.xamlx workflow service running in IIS and have State1, State2 and State3.

Trigger T1 (State 1 -> State 2) listens to "Start" message on Receive Activity.
Trigger T2 (State 2 -> State 3) listens to "Proceed" message on Receive Activity.

The problem I have is that i expect something like InvalidOperationException when my state machine is in State 1 and "Proceed" message is coming. However, it looks like it is just awaiting in queue and fails with timeout exception.

How do I get an expected behavior here?


Solution

  • I'm just pasting here an answer I got from Jim Carley - MSFT at MSDN forums so that it doesn't disappear after some years.

    Alexander,

    You are running into a situation where the workflow has both "protocol bookmarks" and "non-protocol bookmarks". "Protocol bookmarks" are created by the messaging activities (e.g Receive). "Non-protocol bookmarks" are bookmarks that are unrelated to messaging activities. And State activities create non-protocol bookmarks internally.

    This issue was reported when using "Pick" also - https://social.msdn.microsoft.com/Forums/vstudio/en-US/275f7817-1ec7-433c-89ba-fe48afd1dae8/wf4-wcf-send-message-at-wrong-time?forum=wfprerelease

    Here is an excerpt from my explanation on that thread:

    The root cause of the difference in behavior (timeout) when using a Pick or State activity to encapsulate the Receive for OperationB is that Pick and State activities create internal bookmarks that are unrelated to the bookmarks created by Receive activities.

    The bookmarks created by Receive activities (let’s call them “protocol bookmarks”) are treated in a special way in order to preserve the messaging protocol implemented by the workflow service.

    Imagine a scenario where the service has the messaging “protocol” of Operation1 followed by Operation2:

    Receive(Operation1)

    SendReply(Operation1)

    DoSomeOtherWork

    Receive(Operation2)

    SendReply(Operation2)

    If DoSomeOtherWork has asynchronous operations that may cause the workflow instance to go idle, they are going to create non-protocol bookmarks. These bookmarks will get resumed thru some other means, outside of the messaging protocol for the workflow service.

    So it is possible for Operation1 to complete and for the client to send Operation2 before DoSomeOtherWork is complete. We don’t want to reject Operation2 immediately. Instead, we hang onto the Operation2 message in hopes that the work being done by DoSomeOtherWork completes and the bookmark associated with that work gets resumed. Once DoSomeOtherWork completes, the protocol bookmark for Receive(Operation2) will get created and now the message from the client can successfully get processed.

    While DoSomeOtherWork is still outstanding, the message for Operation 2 is received. It discovers that there is no protocol bookmark for Operation2. We then check to see if there are any non-protocol bookmarks outstanding for the instance. If there are (as is the case when DoSomeOtherWork is still outstanding), we hang on to the message. But if there are no other non-protocol bookmarks, we immediately reject the message as being out of order.

    Starting in .NET 4.6, there is an AppSetting that you can specify in the web.config file of your service that controls how non-protocol bookmarks and out-of-order messages are dealt with. To configure the AppSetting, add this to your web.config file:

    The value of this "FilterResumeTimeoutInSeconds" specifies the length of time (in seconds) the workflow runtime hangs on to an out-of-order message before it times out. The default value is 60. A value of 0 specifies that it should not wait at all and reject out-of-order message with fault with the text:

    Operation '' on service instance with identifier '' cannot be performed at this time. Please ensure that the operations are performed in the correct order and that the binding in use provides ordered delivery guarantees.

    If the value is greater than 0, then you will still get a timeout exception after the specified time expires.

    Again, this new AppSetting is available starting in .NET 4.6. And this all assumes that BufferedReceive is NOT being used.

    This is also documented here: http://blogs.msdn.com/b/dotnet/archive/2015/07/20/announcing-net-framework-4-6.aspx?PageIndex=2