Handling WF 4.0 long running activity using TPL

I created an activity which executes a web request and stores the result into the database. Usually this process takes about 1 hour and it makes workflow engine to behave abnormally. I found out that for these long running activities I should write some different code so that the workflow engine thread won't be blocked.

Studying some blogs about writing long running activities I understand that I should use Bookmark concept. But I didn't any solution using TPL and Task.

Is this code correct for handling a long running activity using Tasks?

public sealed class WebSaveActivity : NativeActivity
{
    protected override void Execute(NativeActivityContext context)
    {
        context.CreateBookmark("websave", (activityContext, bookmark, value) =>
        {

        });

        Task.Factory.StartNew(() =>
        {
            GetAndSave(); // This takes 1 hour to accomplish.
            context.RemoveBookmark("websave");
        });

    }

    protected override bool CanInduceIdle 
    {
        get
        {
            return true;
        }
    }
}

1 answer

  • answered 2017-06-17 18:52 Peter Bons

    No, that is not the way bookmarks should be used. A bookmark is used when the workflow has to wait for input from an external process.

    For example: I have a document approval workflow and at some point in time the workflow has to wait for a human reviewer to give an OK on the document. Instead of keeping the workflow instance in memory the workflow will be idled and activated again by the runtime when ResumeBookmark is called.

    In your situation your workflow cannot be idled since it has an operation running in its context. That operation is your task that, by the way, is a fire-and-forget tasks so critical failures cannot be handled by the WF.

    Now, for a possible solution you might consider to have an other process call the GetAndSave method and have that process ultimately call the ResumeBookmark on the WF so the workflow can be idled by the runtime. That process could even be the same process that hosts your workflow.

    For an example see this blogpost. Just image that instead of waiting for a human to enter something in the console your long running task is performed.

    You did not specify what comes after your activity but do note that it is possible to return data back to the workflow when the bookmark is resumed. So any result of the GetAndSave, even if it is just an error code you can use to decide how to further go along with the other activities in your workflow.

    Hope this makes sense to you and you see what I try to outline as a possible solution.

    EDIT A quick note about using Tasks or async/await in WF. There are AFAIK no methods to override that return Tasks so you either have to make them block by using .Wait() or .Result or forget about it. Because if you cannot await them bad things will happen during workflow execution because other activities might be started before the one using Tasks has completed its work.

    When the WF runtime was developed the whole concept of Tasks was still very young so the WF runtime did not / does not cater for them.