none
Why Workflow Exceptions are not caught when thrown from Cancel? RRS feed

  • Question

  • I have subscribed to OnUnhandledException on WorkflowApplication for any unexpected exception.

    The OnUnHandledException callback is received for all exceptions inside workflow but cancel case.

    Consider the WorkflowApplication.BeginCancel(...,...) is called which eventually invokes the cancel on activities, now if exception is thrown from within Activity(say SampleActivity in below example) upon cancel call then OnUnhandledException callback is not received.

    I was expecting that the protocol is same throughout i.e. if there is an exception, no matter from where, the UnhandledException callback should receive a call.

    It would be really great if you can share documentation link which mention this unique case of cancel or suggest if I need to do anything extra to get this callback.

    Following is the sample code:
    PS:Notice i am intentionally throwing exception from protected override void Cancel(NativeActivityContext context){}.

    class Program
        {

            static void Main(string[] args)
            {
                Console.WriteLine("Enter values for following operations");
                Console.WriteLine("c: Cancel");
                Console.WriteLine("p: Progress. This is a multiple resume operation you can press p as many times you want untill workflow is completed or aborted!!");
                Console.WriteLine("f: Finish");
                Console.WriteLine("0 i.e Zero: Exit");
                Console.WriteLine("=============================================");

                var wf = CreateWorkflow();
                string a = Console.ReadLine();
                while (a.ToString() != "0")
                {

                    switch (a.ToString().ToLower())
                    {
                        case "c":
                            {

                                //wf.Cancel();
                                wf.BeginCancel(AsyncCallBackMethod, null);
                                break;
                            }
                        case "f":
                            {
                                wf.ResumeBookmark("Finish", null);
                                Console.WriteLine("Workflow completed press 0 to exit.");
                                break;
                            }
                        case "p":
                            {
                                wf.ResumeBookmark("Progress", "Shweytank");
                                break;
                            }
                        default:
                            {
                                Console.WriteLine("Not a valid input");
                                break;
                            }
                    }
                    Console.WriteLine("");
                    Console.WriteLine("=============================================");
                    Console.WriteLine("==============Enter next operation===========");
                    a = Console.ReadLine();
                }

                Console.WriteLine("Enter to exit");
                Console.Read();
            }

            private static void AsyncCallBackMethod(IAsyncResult ar)
            {
                Console.WriteLine("Cancel callback");
            }

            static WorkflowApplication CreateWorkflow()
            {
                WorkflowApplication wfApp = new WorkflowApplication(new SampleActivity()) { SynchronizationContext = new SynchronousSynchronizationContext() };

                // Handle the desired lifecycle events.
                wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
                {
                    if (e.CompletionState == ActivityInstanceState.Faulted)
                    {
                        Console.WriteLine("Workflow {0} Terminated.", e.InstanceId);
                        Console.WriteLine("Exception: {0}\n{1}",
                            e.TerminationException.GetType().FullName,
                            e.TerminationException.Message);
                    }
                    else if (e.CompletionState == ActivityInstanceState.Canceled)
                    {
                        Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
                    }
                    else
                    {
                        Console.WriteLine("Workflow {0} Completed.", e.InstanceId);
                    }

                    Console.Read();
                };

                wfApp.Aborted = delegate (WorkflowApplicationAbortedEventArgs e)
                {
                    Console.WriteLine("Workflow {0} Aborted.", e.InstanceId);
                    Console.WriteLine("Exception: {0}\n{1}",
                        e.Reason.GetType().FullName,
                        e.Reason.Message);
                };

                wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
                {
                    Console.WriteLine("Workflow {0} Idle.", e.InstanceId);
                };

                wfApp.PersistableIdle = delegate (WorkflowApplicationIdleEventArgs e)
                {
                    return PersistableIdleAction.Unload;
                };

                wfApp.Unloaded = delegate (WorkflowApplicationEventArgs e)
                {
                    Console.WriteLine("Workflow {0} Unloaded.", e.InstanceId);
                };

                wfApp.OnUnhandledException = delegate (WorkflowApplicationUnhandledExceptionEventArgs e)
                {
                    Console.WriteLine("OnUnhandledException in Workflow {0}\n{1}",
                    e.InstanceId, e.UnhandledException.Message);

                    Console.WriteLine("ExceptionSource: {0} - {1}",
                        e.ExceptionSource.DisplayName, e.ExceptionSourceInstanceId);

                    return UnhandledExceptionAction.Cancel;
                };

                wfApp.Run();

                return wfApp;
            }
        }

        public class SampleActivity : NativeActivity
        {
            protected override sealed bool CanInduceIdle
            {
                get { return true; }
            }

            protected override void Execute(NativeActivityContext context)
            {
                Console.Write("Inside Activite Execute");
                CreateBookmarks(context);
            }

            protected override void Cancel(NativeActivityContext context)
            {
                throw new Exception("Some Exception");
            }

            private void CreateBookmarks(NativeActivityContext context)
            {
                context.CreateBookmark("Progress", OnProgressRecieved, BookmarkOptions.MultipleResume);
                context.CreateBookmark("Finish", OnCalculationFinished);
            }

            protected virtual void OnProgressRecieved(NativeActivityContext context, Bookmark bookmark, object sender)
            {
                var name = sender as string;
                if (string.IsNullOrEmpty(name))
                {
                    throw new Exception();
                }
                Console.WriteLine($"On Activity Progress called. Welcome {name}");
            }

            protected virtual void OnCalculationFinished(NativeActivityContext context, Bookmark bookmark, object sender)
            {
                Console.WriteLine($"On Activity finished called.");

                context.RemoveAllBookmarks();
            }
        }

        public class SynchronousSynchronizationContext : SynchronizationContext
        {
            public override void Post(SendOrPostCallback d, object state)
            {
                d(state);
            }
        }



    Wednesday, September 25, 2019 6:24 AM