Search code examples
c#task-parallel-libraryworkflow-foundation-4workflow-foundation

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;
        }
    }
}

Solution

  • I just saw your related question here: How to write a long running activity to call web services in WF 4.0

    Another way is to implement your activity is as an AsyncCodeActivity:

    namespace MyLibrary.Activities
    {
        using System;
        using System.Activities;
    
        public sealed class MyActivity : AsyncCodeActivity
        {
            protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
            {
                var delegateToLongOperation = new Func<bool>(this.LongRunningSave);
                context.UserState = delegateToLongOperation;
                return delegateToLongOperation.BeginInvoke(callback, state);
            }
    
            protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
            {
                var longOperationDelegate = (Func<bool>) context.UserState;
                var longOperationResult = longOperationDelegate.EndInvoke(result);
    
                // Can continue your activity logic here.
            }
    
            private bool LongRunningSave()
            {
                // Logic to perform the save.
                return true;
            }
        }
    }
    

    The workflow instance stays in memory, but at the very least the workflow runtime can handle its normal scheduling tasks without one of its threads being taken up by a long running process.