Welcome

You have reached the blog of Keith Elder. Thank you for visiting! Feel free to click the twitter icon to the right and follow me on twitter.

Cannot Invoke Workflows From Workflows Hosted in Asp.Net with Manual Scheduler

Posted by Keith Elder | Posted in Asp.Net, Web Services, Workflow Foundation | Posted on 30-07-2007

WasherAs you can see I’ve included my famous rusty washer on this post to denote a problem.  I was having a conversation with a team member about Workflow Foundation who was having a problem.  The problem was “Hey Keith, have you ever had workflows in Workflow Foundation invoke workflows?”.  Answer:  I  have not, but let’s talk about it, what’s the problem?

 My team member started explaining that they have a need for workflows to call other workflows.  Sounds simple enough since their is a pre-packaged activity in WF to do just this in the toolbox.

He further elaborated that when they invoke the workflows the workflow runtime dies.  At first I was perplexed, but then knowing they were hosting it in Asp.Net web service I asked which scheduler they were running.  He replied ManualWorkflowSchedulerService.  Ah ha!

The problem is a catch 22 and honestly I don’t have a solution for this.  The problem is that when hosting a workflow with the ManualWorkflowSchedulerService it cannot invoke other workflows.  I guess it makes sense since it cannot spawn a new thread, after all it is trying to process a new instance of a workflow on the same thread. (at least that is my theory) Is there a work around?  I don’t know.  Is it a problem, yeah!

To compound matters the only way to reliably host workflow foundation in Asp.Net service is with the ManualWorkflowSchedulerService.  Well, ok it isn’t the only way, but if there are problems using the default scheduler in Asp.Net.  Here is Paul Andrew’s post about a fix coming in WF where he explains the differences.

The DefaultWorkflowSchedulerService is the out-of-box Windows Workflow Foundation scheduler used by the runtime to execute workflow instances on new CPU threads.  These threads are leveraged from the.NET thread pool associated with the host application. This works well unless the workflow runtime is hosted in ASP.NET.  Because of the limited thread resource in the IIS server, we would be allocating an additional unnecessary thread to execute the workflow instance for every HTTP request that required workflow execution. The ManualWorkflowSchedulerService was developed to address this issue by allowing the thread that is processing the HTTP request to execute a workflow instance. Unfortunately in WF Beta 2.2 and prior, it didn’t handle the correct processing of timer events (i.e. delay activities).

When doing Asp.Net Web Services the only reliable way to return output parameters, catch exceptions, logging, handle faults, etc is to run the workflow on the same thread synchronously using the ManualWorkflowSchedulerService.  I’ve tried every which way I knew to get the default scheduler to work but couldn’t.  If you think about it, it makes sense since web services receive a message, then typically return a message.  With the default scheduler it is hard to bubble up exceptions properly so they can be logged or caught and handle gracefully in a service.

To test this theory I created a console workflow app and created two workflows with one calling the other like this.

image

I then wired up the ManualWorkflowSchedulerService in the Program.cs like this and run it.  It doesn’t run. If I comment out the manual scheduler and use the default, it works fine.

static void Main(string[] args)

        {

            try

            {

                using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())

                {

                    ManualWorkflowSchedulerService service = new ManualWorkflowSchedulerService(false);

                    workflowRuntime.AddService(service);

                    AutoResetEvent waitHandle = new AutoResetEvent(false);

                    workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) { waitHandle.Set(); };

                    workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)

                    {

                        Console.WriteLine(e.Exception.Message);

                        waitHandle.Set();

                    };

 

                    WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1));

                    ManualWorkflowSchedulerService scheduler = workflowRuntime.GetService<ManualWorkflowSchedulerService>();

                    instance.Start();

                    scheduler.RunWorkflow(instance.InstanceId);

                    waitHandle.WaitOne();

                }

            }

            catch (Exception ex)

            {

                Console.WriteLine(ex.Message);

                Console.ReadLine();

            }

 

 So the catch 22 is I need to use the manual scheduler with Asp.Net Web Services but I can’t invoke other workflows if I do.  If I use the default scheduler, I can’t scale my service.  The “solution” we are going with right now for the work around is to host the service in WCF as a Windows Service outside of IIS.    The bad part of this is you can’t really scale this either.  That’s the nice thing about using IIS is it really is an app server.  Clustering and those things are widely available.  If you build your own windows service you lose all of that baked in functionality.  I think this is a HUGE rusty washer.   Anyone faced this challenge?  How did you solve it?  Am I off my rocker on this one?  Feedback would be appreciated.

UPDATE (8.28.2007)

One of the developers at work (Joe Brach) solved this problem since he was running into the same issue.  I thought I’d post his example of his fix since several people have been emailing me if I found a fix.  The “fix” is you create your own class that extends WorkflowRuntimeService.  Here is Joe’s fix:

    public class StartWorkFlowService: WorkflowRuntimeService

    {

        public Guid StartWorkflow(Type workflowType, Dictionary<string, object> inparms

        {

            //Get an instance of the runtime

            WorkflowRuntime wr = this.Runtime;

 

            //Create the type of Workflow

            WorkflowInstance wi = wr.CreateWorkflow(workflowType, inparms);

 

            //Start the WorkFlow

            wi.Start();

 

            //Get the Manual Workflow Service

            ManualWorkflowSchedulerService ss = wr.GetService<ManualWorkflowSchedulerService>();

 

            if (ss != null)

            {

                ss.RunWorkflow(wi.InstanceId);

            }

            return wi.InstanceId;

        }

    }

Comments (3)

HI
I am getting object referance not set to instance of object at this.Runtime line

i am implementig this scenario in WCF.

Yes, see the update I added.

I’m facing the same problem… Have you found a solution?

Write a comment