I am using Microsoft Windows Workflow Foundation in my Windows based application.
I have subscribed to OnUnhandledException
on workflow for any unexpected exception.
The OnHandledException
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
(Derived from Native activity) upon cancel call then OnUnhandledException callback is not received.
I know that I can still get a CancelCallBack
for BeginCancel
and I can do the same stuff as OnUnhandledException
, however I would want to refrain myself from doing so. Also code is more understandable if there is standard protocol - (if there is an exception, no matter from where, the UnhandledException
callback should receive a call).
If such is the protocol provided by Microsoft for Workflow, 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.
The following is the sample code
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", "Hello World");
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);
}
}