locked
CallExternalMethod executing too early RRS feed

  • Question

  • I have a CEM that passes an object altered by the previous activity back to the host.

    However, the previous activity uses a transaction service to do the work in an class that implements IPendingWork.

    The Commit on the workitem is being called after the CEM fires.  The idea was the first activity runs adds item to Workbatch, that does work in transaction.  Then the CEM sends the resultant object (which is bound to a workflow level object) back to the host.

    Is there any way to set a conditional delay on the CEM. 

    TIA

    MattC

    Thursday, April 9, 2009 9:56 AM

Answers

  • You have a few options.

    Is your previous activity a custom activity? If so then just don't return closed until all processing is finished. If you want your workflow to persist while processing then you could create a bookmark using queues and return executing from your execute message and this would allow the workflow to persist until an item gets added to your activities queue if it becomes idle.

    Another optin is add a HandleExternalEvent activity before your CEM that waits for notificaiton from your previous activity that it's completed it's work.

    Another option is to have a while loop that just checks for some status from you previous event and then executes a delay until the status is what it needs to be for calling CEM.


    If none of that works the please provide more details about previous activity.
    If my response answers your question, please mark it as the "Answer" by clicking that button above my post.
    Thursday, April 9, 2009 12:58 PM
    Moderator
  • I may be missing the point here, but you can mark your custom activity with the [PersistOnClose] attribute, which will persist the workflow, but in the process, it'll also force any pending workbatch items to be run and committed. Then, your data should be in a consistent state before the CEM activity starts.
    Thursday, April 9, 2009 3:49 PM

All replies

  • You have a few options.

    Is your previous activity a custom activity? If so then just don't return closed until all processing is finished. If you want your workflow to persist while processing then you could create a bookmark using queues and return executing from your execute message and this would allow the workflow to persist until an item gets added to your activities queue if it becomes idle.

    Another optin is add a HandleExternalEvent activity before your CEM that waits for notificaiton from your previous activity that it's completed it's work.

    Another option is to have a while loop that just checks for some status from you previous event and then executes a delay until the status is what it needs to be for calling CEM.


    If none of that works the please provide more details about previous activity.
    If my response answers your question, please mark it as the "Answer" by clicking that button above my post.
    Thursday, April 9, 2009 12:58 PM
    Moderator
  • Ryan,

    Thanks for replying.

    The Activity is indeed a custom activity, it is also inside an Event Driven event that has a EEH infront of it.

    It's doing something like this:

    protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
            {
                if (Application != null)
                {
                    IApplicationWorkflowTransactionService txnService = executionContext.GetService<IApplicationWorkflowTransactionService>();
                   
                    ApplicationCreatedWorkItem workItem = new ApplicationCreatedWorkItem();
                     txnService.CreateApplication(workItem, Application);
                }
    
                return ActivityExecutionStatus.Closed;
            }
    where  txnService.CreateApplication(workItem, Application); does:

    WorkflowEnvironment.WorkBatch.Add(workItem, application);
    How do I wait until this workitem has done it's job till the CEM executes.  I'd like to use the ActivityExecutionStatus rather than some local status variable ideally.

    TIA

    MattC


    Thursday, April 9, 2009 1:06 PM
  • You'd need to use bookmarks via queues for this. Here is an example of how to wire that together in your custom activity

    using System;
    
    using System.Workflow.ComponentModel;
    
    using System.Workflow.Runtime;
    
    
    
    namespace EssentialWF.Activities
    
    {
    
      public class ReadLine : Activity
    
      {
    
        private string text;
    
        public string Text
    
        {
    
          get { return text; }
    
        }
    
    
    
        protected override ActivityExecutionStatus Execute(
    
          ActivityExecutionContext context)
    
        {
    
          WorkflowQueuingService qService =
    
            context.GetService<WorkflowQueuingService>();
    
    
    
          WorkflowQueue queue =
    
            qService.CreateWorkflowQueue(this.Name, true);
    
          queue.QueueItemAvailable += this.ContinueAt;
    
    
    
          return ActivityExecutionStatus.Executing;
    
        }
    
    
    
        void ContinueAt(object sender, QueueEventArgs e)
    
        {
    
          ActivityExecutionContext context =
    
            sender as ActivityExecutionContext;
    
    
    
          WorkflowQueuingService qService =
    
            context.GetService<WorkflowQueuingService>();
    
    
    
          WorkflowQueue queue = qService.GetWorkflowQueue(this.Name);
    
          text = (string) queue.Dequeue();
    
          qService.DeleteWorkflowQueue(this.Name);
    
    
    
          context.CloseActivity();
    
        }
    
      }
    
    }
    
    
    
    

    See full details of how to do this type of thing here: http://www.informit.com/articles/article.aspx?p=680838&seqNum=3 and I highly recomend the book that this code sample comes form.

    This Custom activity will not finish executing until after a new item is added to it's queue. Once a new item is added it will cause ContinueAt to execute. So in your case you'd need a way to wire up the transaction service so that it would add an item to the custom activity queue when it's finished processing. If that's not viable because you can't change the code and it's asyncronous then you'd need to try something like the while loop techinique described above.


    If my response answers your question, please mark it as the "Answer" by clicking that button above my post.
    Thursday, April 9, 2009 1:20 PM
    Moderator
  • It's not far off although it's not the transaction service that needs to add the item to the queue as I need the custom activity to wait until Commit has been called on the workitem added to the workbatch.

    This doesn't have access to the current ActivityExecutionContext to get at the queue to add the item.

    This is all Sychronous (ManualWorkflowScheduler) so bookmarks is the way to go I think...I just can't get the IPendingWork item to get access to the GetWorkflowQueue????
    Thursday, April 9, 2009 1:58 PM
  • Here is the client code that adds to the queue for the above example if that helps. Seems to me like you could wire it up. I'd encourage you to read the page that I posted a link to above. I'm pretty sure you'll find some good options there.

    using System;
    using System.Workflow.ComponentModel.Compiler;
    using System.Workflow.Runtime;
    using System.Xml;
    
    class Program
    {
      static void Main()
      {
        using (WorkflowRuntime runtime = new WorkflowRuntime())
        {
          TypeProvider tp = new TypeProvider(null);
          tp.AddAssemblyReference("EssentialWF.dll");
          runtime.AddService(tp);
    
          runtime.StartRuntime();
    
          runtime.WorkflowIdled += delegate(object sender,
            WorkflowEventArgs e)
          {
            Console.WriteLine("WF program instance " +
              e.WorkflowInstance.InstanceId + " is idle");
          };
    
          runtime.WorkflowCompleted += delegate(object sender,
            WorkflowCompletedEventArgs e)
          {
            Console.WriteLine("WF program instance " +
              e.WorkflowInstance.InstanceId + " completed");
          };
    
          WorkflowInstance instance = null;
          using (XmlTextReader reader = new XmlTextReader("Read.xoml"))
          {
            instance = runtime.CreateWorkflow(reader);
            instance.Start();
          }
    
          string text = Console.ReadLine();
          instance.EnqueueItem("r1", text, null, null);
    
          // Prevent Main from exiting before
          // the WF program instance completes
          Console.ReadLine();
    
          runtime.StopRuntime();
        }
      }
    }
    




    If my response answers your question, please mark it as the "Answer" by clicking that button above my post.
    Thursday, April 9, 2009 2:03 PM
    Moderator
  • I could pass the current ActivityExecutionContext as a parameter on the ctor for the IPendingWork item being added to the batch so it can add it's data item to the queue to be picked up.

    Although, it doesn't feel right right passing contexts around.  The last example you gave is too high level as the item being enqueue is at a stage where access to the current workflow instance ins't available.

    The link you posted is good I had a read but it seems to only deal with examples where the stimulus to fire the ContinueAt method is external to the current process.  My case is a little awkward as I've passed an item of work to be run in the first activity where I need the resultant object in the second. 

    Although the entire process is Synchronous, the order of events isn't entirely how I need it.

    Currently:

    CustomActivity - Creates WorkItem adds domain entity to be altered and passes to transactionSvc that .
    CEM fires
    Workitem.Commit in workbatch - domain entity altered

    Ideally:
    CustomActivity - Creates WorkItem adds domain entity to be altered and passes to transactionSvc that .
    Workitem.Commit in workbatch - domain entity altered
    CEM fires

    Thursday, April 9, 2009 2:20 PM
  • I may be missing the point here, but you can mark your custom activity with the [PersistOnClose] attribute, which will persist the workflow, but in the process, it'll also force any pending workbatch items to be run and committed. Then, your data should be in a consistent state before the CEM activity starts.
    Thursday, April 9, 2009 3:49 PM
  • You're not missing the point Jim, you've hit the nail on the head.

    Many thanks Ryan and Jim.
    Tuesday, April 14, 2009 8:38 AM