1

I have a service based on the following contract (abridged):

[ServiceContract]
public interface ISchedulerService
{
    [OperationContract]
    void Process(bool isForced);
}

The germane (IMHO) part of the implementation is:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, UseSynchronizationContext = false)]
public class SchedulerService : ISchedulerService
{
    public async void Process(bool isForced)
    {
        try
        {
            var prevStatus = Status;
            Status = SchedulerStatus.Processing;
            await ProcessListings();
            Status = prevStatus;
        }
        catch (Exception ex)
        {
            throw new FaultException(ex.Message);
        }
    }

    private static Task ProcessListings()
    {
        throw new Exception("ProcessListings failed.");           
        return Task.Delay(5000);
    }
}

The service is currently hosted in a tiny console app:

class Program
{
    private static readonly SchedulerService Scheduler = SchedulerService.Instance;
    private static ServiceHost schedulerHost;

    protected static void OnStart()
    {
        try
        {
            if (schedulerHost != null)
            {
                schedulerHost.Close();
                schedulerHost = null;
            }
            schedulerHost = new ServiceHost(Scheduler);
            schedulerHost.Open();
            Scheduler.Start();
        }
        catch (Exception ex)
        {
            //EventLog.WriteEntry("Exception: " + ex.Message);
            throw;
        }
    }
}

And, finally, the client:

private readonly SchedulerServiceClient _proxy= new SchedulerServiceClient();
...
void ExecuteProcessNowCommand()
{
    try
    {
        _proxy.Process(true);
    }
    catch (Exception ex)
    {
        if (exception is SchedulerException)
        {
            MessageBoxFacility.ProcessingError((SchedulerException)exception);
        }
    }
}

where SchedulerServiceClient is the proxy that is generated when I add a service reference. Up until now, I have successfully hosted the WCF inside a live Windows Service and tested non-exception functionality. Everything was fine until I added exception handling. I know this is a complex scenario, but most examples I have seen suggest that the FaultException would at least be caught by the general Exception handler in the client. My debugger makes me guess the exception doesn't even make it to the proxy, and it remains unhandled in the MVC code. When I hit 'continue' enough, I end up at a screen telling me the stack contains only external code. This is the stack trace for that external code:

at System.Runtime.CompilerServices.AsyncMethodBuilderCore.<ThrowAsync>b__1(Object state)
at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()</ExceptionString></Exception></TraceRecord>

This makes me think my use of async in the MCV service might me playing a role, but then maybe it's only an innocent middleman. Please help me try and determine why the exception is not even propagated to the client.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
ProfK
  • 49,207
  • 121
  • 399
  • 775

1 Answers1

2

Exceptions in async methods are stored in the returned task. async void methods don't return a task and so the exception is thrown on a ThreadPool thread tearing down the AppDomain.

Don't use async void anywhere other than event handlers.

If you return a task you can await that task and the exception will be rethrown.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • The exception is properly caught by the method that waits on the async method, i.e. `Process` does catch the exception thrown by `ProcessListings`. The `AppDomain` still seems fine while `Process` throws the exception it catches. – ProfK Mar 06 '15 at 15:01
  • @ProfK `ProcessListings` isn't an `async` method. It just returns a task. `Process` is the `async` method throwing the exception, which is not caught. – i3arnon Mar 06 '15 at 15:04
  • Aha, thanks! I have just removed all asynchrony, and the exception gets to the client. So, should I now rather create the task in `Process`, and `await` the generated async method in the proxy? – ProfK Mar 06 '15 at 15:11
  • @ProfK pretty much. Don't create a task, just change the return value and the compiler will generate that for you. You can look at this for more info: http://www.codeproject.com/Articles/613678/Task-based-Asynchronous-Operation-in-WCF – i3arnon Mar 06 '15 at 15:15
  • Thank you. I have begun another long climb of learning. I'm still near the bottom of the one where you mix WPF, timers, WCF, and a Windows Service. – ProfK Mar 06 '15 at 17:32