How can I stop IConnectableObservable.Wait() from swallowing unhandled exceptions? - c#

I have a console application using Rx.NET.
I need to block until an IConnectableObservable has completed, for which I am using IConnectableObservable.Wait().
When an unhandled exception is thrown, it is swallowed and the application hangs. I want the application to crash, and the stack track to be printed to the console.
I do not want to add an OnError handler to my IConnectableObserver because this loses the original stack trace.
I have tried using the .Wait() method on the unpublished observable, but this re-subscribes which causes undesirable behaviour.
I have tried using .GetAwaiter().GetResult() instead, but this has the same problem.
var connectable = myObservable.Publish();
connectable.Subscribe(myObserver1);
connectable.Subscribe(myObserver2);
connectable.Connect();
connectcable.Wait();
How can I wait for an IConnectableObservable to complete while retaining typical unhandled exception behaviour?

There's some misdirection in the chain of events here.
The error isn't being swallowed - far from it, it's being re-thrown.
The usual suspects are some weird concurrency and scheduling issues, but nobody suspects the Subscribe method.
When you call Subscribe with something other than your own IObserver<T>, you're creating an AnonymousObserver with these default actions.
new AnonymousObserver<T>(Ignore, Throw, Nop)
which is effectively
new AnonymousObserver<T>(_ => {}, exn => throw exn, () => {})
The default error handler will throw the error on whatever context you're observing on. Yikes. Sometimes it might be the AppDomain timer, or on a pooled thread, and since it can't be handled, your application goes down.
So if we change the sample to provide in a dummy handler,
var myObservable = Observable.Interval(TimeSpan.FromMilliseconds(100)).Take(4).Concat(Observable.Throw(new Exception(), 1L));
var connectable = myObservable.Publish();
connectable.Subscribe(Console.WriteLine, exn => Console.WriteLine("Handled"));
connectable.Subscribe(Console.WriteLine, exn => Console.WriteLine("Handled"));
connectable.Connect();
try
{
connectable.Wait();
}
catch (Exception)
{
Console.WriteLine("An error, but I'm safe");
}
You can handle the error in the Wait like you'd expect to.

Related

Exception Handling In DataBlocks

I am trying to understand exception handling in TPL.
The following code seems to swallow exceptions:
var processor = new ActionBlock<int>((id) => SomeAction(id), new ExecutionDataflowBlockOptions { ... });
async Task SomeAction(int merchantID)
{
//Exception producing code
...
}
And listening to TaskScheduler.UnobservedTaskException events does not receive anything either.
So, does this mean the action block does a try-catch in itself when running the actions?
Is there any official documentation of this somewhere?
Update
The exception handling behavior of DataFlow blocks is explained in Exception Handling in TPL DataFlow Networks
**Original
This code doesn't swallow exceptions. If you await the block to complete with await processor.Completion you'll get the exception. If you use a loop to pump messages to the block before calling Complete() you need a way to stop the loop too. One way to do it is to use a CancellationTokenSource and signal it in case of exception:
void SomeAction(int i,CancellationTokenSource cts)
{
try
{
...
}
catch(Exception exc)
{
//Log the error then
cts.Cancel();
//Optionally throw
}
}
The posting code doesn't have to change all that much, it only needs to check whether
var cts=new CancellationTokenSource();
var token=cts.Token;
var dopOptions=new new ExecutionDataflowBlockOptions {
MaxDegreeOfParallelism=10,
CancellationToken=token
};
var block= new ActioBlock<int>(i=>SomeAction(i,cts),dopOptions);
while(!token.IsCancellationRequested && someCondition)
{
block.Post(...);
}
block.Complete();
await block.Completion;
When the action throws, the token is signaled and the block ends. If the exception is rethrown by the action, it will be rethrown by await block.Completion as well.
If that seems convoluted, it's because that's somewhat of an edge case for blocks. DataFlow is used to create pipelines or networks of blocks.
The general case
The name Dataflow is significant.
Instead of building a program by using methods that call each other, you have processing blocks that pass messages to each other. There's no parent method to receive results and exceptions. The pipeline of blocks remains active to receive and process messages indefinitely, until some external controller tells it to stop, eg by calling Complete on the head block, or signaling the CancellationToken passed to each block.
A block shouldn't allow unhandled exceptions to occur, even if it's a standalone ActionBlock. As you saw, unless you've already called Complete() and await Completion, you won't get the exception.
When an unhandled exception occurs inside a block, the block enters the faulted state. That state propagates to all downstream blocks that are linked with the PropagateCompletion option. Upstream blocks aren't affected, which means they may keep working, storing messages in their output buffers until the process runs out of memory, or deadlocks because it receives no responses from the blocks.
Proper Failure Handling
The block should catch exceptions and decide what to do, based on the application's logic:
Log it and keep processing. That's not that different from how web application's work - an exception during a request doesn't bring down the server.
Send an error message to another block, explicitly. This works but this type of hard-coding isn't very dataflow-ish.
Use message types with some kind of error indicator. Perhaps a Success flag, perhaps an Envelope<TMessage> object that contains either a message or an error.
Gracefully cancel the entire pipeline, by signaling all blocks to cancel by signaling the CancellationTokenSource used to produce the CancellationTokens used by all blocks. That's the equivalent of throw in a common program.
#3 is the most versatile option. Downstream blocks can inspect the Envelope and ignore or propagate failed messages without processing. Essentially, failed messages bypass downstream blocks.
Another option is to use the predicate parameter in LinkTo and send failed messages to a logger block and successful messages to the next downstream block. In complex scenarios, this could be used to eg retry some operations and send the result downstream.
These concepts, and the image, come from Scott Wlaschin's Railway Oriented Programming
The TaskScheduler.UnobservedTaskException event is not a reliable/deterministic way to handle exceptions of faulted tasks, because it's delayed until the faulted task is cleaned up by the garbage collector. And this may happen long after the error occurred.
The only type of exception that is swallowed by the dataflow blocks is the OperationCanceledException (AFAIK for non-documented reasons). All other exceptions result to the block transitioning to a faulted state. A faulted block has its Completion property (which is a Task) faulted as well (processor.Completion.IsFaulted == true). You can attach a continuation to the Completion property, to receive a notification when a block fails. For example you could ensure that an exception will not pass unnoticed, by simply crashing the process:
processor.Completion.ContinueWith(t =>
{
ThreadPool.QueueUserWorkItem(_ => throw t.Exception);
}, default, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
This works because throwing an unhandled exception on the ThreadPool causes the application to terminate (after raising the AppDomain.CurrentDomain.UnhandledException event).
If your application has a GUI (WinForms/WPF etc), then you could throw the exception on the UI thread, that allows more graceful error handling:
var uiContext = SynchronizationContext.Current;
processor.Completion.ContinueWith(t =>
{
uiContext.Post(_ => throw t.Exception, null);
}, default, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.Default);
This will raise the Application.ThreadException event in WinForms.

IObservable swallows exceptions by default?

I have an IObservable set up, and I'm trying to trigger it to transition from "cold" to "hot". However, by pure dumb luck, I found that somewhere in the IObservable, an unhandled exception is throw.
I immediately freaked out because Reactive Extensions didn't let me know about this unhandled exception.
I guess the stream just terminated, instead. Now I'm worried that Rx Extensions is going to be swallowing other exceptions.
This is the code I'm currently using to trigger the IObservable to be "hot". I would expect that any unhandled exceptions that occur inside the IObservable to bubble up and be thrown here. But they are not.
var observable = Observable.Create<>(async a =>
{
...
a.OnNext();
...
a.OnCompleted();
});
observable = observable.Do(onNext: ...,
onCompleted: async () =>
{
// This throws the unhandled exception
await MethodThatThrowsExceptionAsync();
});
// I would expect any exceptions inside the IObservable to bubble up and be rethrown here.
await observable.LastOrDefaultAsync();
Am I doing something wrong? Is this expected behavior? This seems extremely error-prone if it is.
Do is for expressing side-effects on the notification, and not to actually modify the notifications themselves. Throwing errors in Do is generally not advised.
If we break this down, you want to run a task when the observable is completed, in which you may throw an exception, which bubbles down.
There's an operator which can shunt another observable once it's completed - Concat.
var observable = Observable.Create<int>(async a =>
{
await Task.Delay(1000);
a.OnNext(0);
a.OnCompleted();
});
observable = observable.Concat(Observable.FromAsync(async () =>
{
await Task.Delay(1000); //simulate work
// This throws the unhandled exception
throw new Exception("I'm from async");
return 1; //type inference
}));
// This now throws
await observable.LastOrDefaultAsync();
P.S.
Be careful when using async/await in place of Action<>.
They fail silently.
Async void methods considered harmful.

Exceptions thrown in a task only get handled if the code is optimized

Update:
The below behavior only occurs when using the Debug configuration (not optimizing the code). When I change the configuration to Release or tick the 'Optimize code' checkbox in the Build properties, it works just fine.
I'm trying to catch exceptions which occur within a task using Task.ContinueWith as explained in this answer, but the exceptions are not getting handled. Here's a screenshot.
You can reproduce using the following code:
var task = Task.Factory.StartNew(() => { throw new Exception("Oops"); });
task.ContinueWith(t => { Console.WriteLine(t.Exception.Message); },
TaskContinuationOptions.OnlyOnFaulted);
I've also tried the following:
var task = Task.Factory.StartNew(() => { throw new Exception("Oops"); });
task.ContinueWith(t =>
{
if (task.IsFaulted) Console.WriteLine(task.Exception.Message);
});
Any idea why the exception isn't handled?
To expand upon our discussion in the comments:
What you have here is a user-unhandled exception (not to be confused with unobserved exception) causing the debugger to break. If you were to run the program built in debug without actually attaching a debugger, it should behave exactly as you expect. The continuation will run, and it will observe the exception from the antecedent task.
From your perspective, you are handling the exception, and if you were to write some vanilla synchronous code such as this:
try
{
throw new Exception("Oops");
}
catch
{
}
... then the debugger is smart enough to work out that the exception is, indeed, handled, and treats it as such.
However, when you're dealing with task continuations, there are no similarly strong guarantees that your exception handling code will run. It can run on the same thread or a different thread, synchronously or asynchronously, or even not at all if the continuation fails to run (which can happen for a number of reasons that the runtime doesn't necessarily have control over). So the safe choice is for the debugger to say "I can't see this exception being handled anywhere in the immediate call stack, therefore it's user-unhandled".
To drive this point home, think about unobserved task exceptions. In .NET 4.0 they could tear down your program a solid minute after actually being thrown. That is how long it took for the runtime to work out with reasonable confidence that no task continuations actually looked at the exception. When you're debugging, you can't wait that long. If something looks unhandled, the safe choice is for the debugger to break immediately.
Finally, I should note that you can modify this behaviour by telling Visual Studio's debugger not to break on particular exception types (OperationCanceledException would be a good candidate as it comes up in asynchronous code a lot) via Debug -> Windows -> Exception Settings.
A Task that has Continuation that throws an exception will have the Exception wrapped as an AggregateException. If you check the InnerException you will see it contains "Oops"
See Exception Handling in TPL for more information

How can I break only on unhandled Exceptions in ContinueWith?

When I write Task-based code, some of my ContinueWith clauses intentionally throw Exceptions (which I catch and handle appropriately), and some of them accidentally throw Exceptions (because of bugs). How can I avoid breaking on the first kind while still breaking on the second kind?
In the code below, I expect the debugger to break on the Unintentional Exception, and NOT on the Intentional Exception (because it is handled later). If I disable "Just My Code" according to this question, then the Intentional Exception doesn't break the debugger (correct), but the Unintentional Exception doesn't break the debugger either (incorrect). If I enable "Just My Code", then the Unintentional Exception does break the debugger (correct), but so does the Intentional Exception (incorrect).
Is there any setting to make Exceptions in ContinueWith clauses work like a normal developer would presumably expect them to?
class Program
{
static void Main(string[] args)
{
//Intentional Exception
Task.Factory
.StartNew(() => Console.WriteLine("Basic action"))
.ContinueWith((t1) => { throw new Exception("Intentional Exception"); })
.ContinueWith((t2) => Console.WriteLine("Caught '" + t2.Exception.InnerException.Message + "'"));
//Unintentional Exception
Task.Factory
.StartNew(() => Console.WriteLine("Basic action"))
.ContinueWith((t3) => { throw new Exception("Unintentional Exception (bug)"); });
Console.ReadLine();
}
}
There are two possible approaches to address this problem. Neither are very satisfying as they essentially rely on the developer detecting 100% their own bugs which is really something the debugger should be doing in a development setting. For either approach, the developer should first turn off "Just My Code" as this is the way to prevent the caught/observed Intentional Exception from causing the debugger to break.
First, as #Peter Deniho mentions in his answer and its comments, the developer can listen for the TaskScheduler.UnobservedTaskException event. The code for this is simple:
TaskScheduler.UnobservedTaskException += (sender, e) =>
{
throw new Exception("An Exception occurred in a Task but was not observed", e.Exception.InnerException);
};
The difficulty with this approach is that this event only fires when the faulted Task's finalizer is called. This happens during garbage collection, but garbage collection may not happen until long after the Exception occurs. To see that this is a serious issue, use the approach mentioned later in this paragraph, but comment out GC.Collect(). In that scenario, the Heartbeats will go on for a very long time before the unobserved Exception is thrown. To deal with this issue, garbage collection must be forced as frequently as you want to make sure your unobserved/Unintentional Exception is reported to the developer. This can be accomplished by defining the following method and calling it at the beginning of the original static void Main:
private static async void WatchForUnobservedExceptions()
{
TaskScheduler.UnobservedTaskException += (sender, e) =>
{
throw new Exception("An Exception occurred in a Task but was not observed", e.Exception.InnerException);
};
//This polling loop is necessary to ensure the faulted Task's finalizer is called (which causes UnobservedTaskException to fire) in a timely fashion
while (true)
{
Console.WriteLine("Heartbeat");
GC.Collect();
await Task.Delay(5000);
}
}
Of course, this has serious performance implications and the fact that this is necessary is a pretty good indicator of fundamentally broken code.
The above approach could be characterized as periodically polling to automatically check Tasks' Exceptions that might have been missed. A second approach is to explicitly check each Task's Exception. That means the developer must identify any "background" Tasks whose Exceptions are not ever observed (via await, Wait(), Result, or Exception) and do something with any Exceptions that are unobserved. This is my preferred extension method:
static class Extensions
{
public static void CrashOnException(this Task task)
{
task.ContinueWith((t) =>
{
Console.WriteLine(t.Exception.InnerException);
Debugger.Break();
Console.WriteLine("The process encountered an unhandled Exception during Task execution. See above trace for details.");
Environment.Exit(-1);
}, TaskContinuationOptions.OnlyOnFaulted);
}
}
Once defined, this method can simply be appended to any Task that might otherwise have unobserved Exceptions if it had bugs:
Task.Factory
.StartNew(() => Console.WriteLine("Basic action"))
.ContinueWith((t3) => { throw new Exception("Unintentional Exception (bug)"); })
.CrashOnException();
The primary drawback of this approach is that each approriate task must be identified (most Tasks will not need or want this modification since most Tasks are used at some future point in the program) and then suffixed by the developer. If the developer forgets to suffix a Task with an unobserved Exception, it will be silently swallowed just like the behavior of the Unintentional Exception in the original question. This means that this approach is less effective at catching bugs due to carelessness even though doing that is what this question is about. In exchange for the reduced likelihood of catching bugs, you do not take the performance hit of the first approach.
I'm pretty sure you can't do what you want here, not exactly.
Note: your code example is a bit misleading. There's no reason the text This line should never print should in fact never print. You don't wait on the tasks in any way, so the output happens before the tasks even get far enough to throw exceptions.
Here's a code example that illustrates what you're asking about, but which IMHO is a bit more robust and which is a better starting point to demonstrate the different behaviors involved:
TaskScheduler.UnobservedTaskException += (sender, e) =>
{
Console.WriteLine();
Console.WriteLine("Unobserved exception: " + e.Exception);
Console.WriteLine();
};
//Intentional Exception
try
{
Console.WriteLine("Test 1:");
Task.Factory
.StartNew(() => Console.WriteLine("Basic action"))
.ContinueWith((t1) => { throw new Exception("Intentional Exception"); })
.ContinueWith(t2 => Console.WriteLine("Caught '" +
t2.Exception.InnerException.Message + "'"))
.Wait();
}
catch (Exception e)
{
Console.WriteLine("Exception: " + e);
}
Console.WriteLine();
//Unintentional Exception
try
{
Console.WriteLine("Test 2:");
Task.Factory
.StartNew(() => Console.WriteLine("Basic action"))
.ContinueWith(
t3 => { throw new Exception("Unintentional Exception (bug)"); })
.Wait();
}
catch (Exception e)
{
Console.WriteLine("Exception: " + e);
}
Console.WriteLine();
Console.WriteLine("Done running tasks");
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Done with GC.Collect() and finalizers");
Console.ReadLine();
The fundamental issue is that even in your first Task example, you're not really handling the exception. The Task class already did that. All you're doing is, by adding the continuation, is adding a new Task that itself won't throw an exception, because of course it doesn't throw an exception.
If you were instead to wait on the original continuation (i.e. the one that throws the exception), you'd find the exception wasn't in fact handled. That task will still throw an exception:
Task task1 = Task.Factory
.StartNew(() => Console.WriteLine("Basic action"))
.ContinueWith(t1 => { throw new Exception("Intentional Exception"); });
Task task2 = task1
.ContinueWith(t2 => Console.WriteLine("Caught '" +
t2.Exception.InnerException.Message + "'"));
task2.Wait();
task1.Wait(); // exception thrown here
I.e. in the above, task2.Wait() is fine, but task1.Wait() still throws the exception.
The Task class does have the idea of whether an exception has been "observed" or not. So even if you don't wait on the first task, as long as you retrieve the Exception property in the continuation (as your example) does, the Task class is happy.
But note that if you change the continuation so that it ignores the Exception property value and you don't wait on the exception-throwing task, when the finalizers run in my version of your example, you will find that there is an unobserved task exception reported.
Task task1 = Task.Factory
.StartNew(() => Console.WriteLine("Basic action"))
.ContinueWith(t1 => { throw new Exception("Intentional Exception"); });
Task task2 = task1
.ContinueWith(t2 => Console.WriteLine("Caught exception"));
task2.Wait();
task1 = null; // ensure the object is in fact collected
So what does all this actually mean in the context of your question? Well, to me it means that the debugger would have to have special logic built into it to recognize these scenarios and understand that even though the exception isn't being handled, it is being "observed" per the rules of the Task class.
I don't know how much work that would be, but it sounds like a lot of work to me. Furthermore, it would be an example of the debugger including detailed information about a specific class in .NET, something that I think would generally be desirable to avoid. In any case, I doubt that work has been done. As far as the debugger is concerned, in both cases you've got an exception and you didn't handle it. It has no way to tell the difference between the two.
Now, all that said…
It seems to me that in your real code you probably aren't actually throwing the plain vanilla Exception type. So you do have the option of using the "Debug/Exceptions…" dialog:
…to enable or disable breaking on specific exceptions. For those that you know, in spite of them actually not being handled, are effectively observed and thus you don't need to break in the debugger when they happen, you can disable breaking on those exceptions in the debugger. The debugger will still break on other exceptions, but will ignore the ones you've disabled.
Of course, if there's a possibility you will accidentally fail to catch/handle/observe one of the exceptions that you otherwise are normally correctly dealing with, then this isn't an option. You'll just have to live with the debugger break and continue.
Not ideal, but frankly should not be a huge problem either; after all, exceptions are by definition for exceptional situations. They shouldn't be used for flow control or inter-thread communication, and so should occur only very rarely. Most tasks should be catching their own exceptions and handling them gracefully, so you should not have to be hitting F5 very often.

handling errors through the async dll stack

I'm trying to handle errors that are passed through 2 dlls I've created. So Console.exe calls dll 1. dll 1 completes an async MQ message read and the handler calls dll 2. If dll 2 errors it passes the Exception (throw) without a problem. But the dll 1 (async) handler catch the throw from dll 2 and give me an unhandled by user message.. I have followed the msdn code to add in the IAsyncResult to keep the hander alive but the issue persists.
can anyone advise on how I should handle this stack and get the handler error returned to the console.exe program so I can present it to the user. Code below:-
Console.exe (snippet)
try
{
_msmq.MSMQ_GetMessage(_msgPath);
//set up the print of the number of queue messages
Console.WriteLine("Main thread: starting a timer");
Timer t = new Timer(ComputeBoundOp, _msgPath, 0, 2000);
Console.Write("Press any key to continue . . .");
Console.ReadKey(true);
t.Dispose(); // Cancel the timer now
}
catch (MessageQueueException _msgQex)
{
Console.WriteLine("An error occurred with the queue:- " + _msgQex);
}
catch (Exception _ex)
{
Console.WriteLine("An error occurred with the queue:- " + _ex);
}
dll 1
public void MSMQ_GetMessage(string _MQ_Path)
{
try
{
//set the correct message queue
MessageQueue _msgQ = new MessageQueue(_MQ_Path, QueueAccessMode.ReceiveAndAdmin);
//set the format of the message queue
_msgQ.Formatter = new XmlMessageFormatter(new Type[] { typeof(_TwitterStreamFeed) });
_msgQ.ReceiveCompleted += new ReceiveCompletedEventHandler(_msgQ_RecieveCompleted);
IAsyncResult _result = _msgQ.BeginReceive();
_asyncList.Add(_result); // asyncList is a global variable of type System.Collections - > this allows the callback to remain open and therefore nit garbage collected while the async thread runs off on it's own
}
catch (Exception _ex)
{
throw new Exception("_msgQ_get Message threw the following error :- " + _ex);
}
}
//method to process message
public void _msgQ_RecieveCompleted(object sender, ReceiveCompletedEventArgs e)
{
try
{
//queue that have received a message
MessageQueue _mq = (MessageQueue)sender;
//get the messge off the queue
Message _mqmsg = _mq.EndReceive(e.AsyncResult);
//set the values back into a formatted struct
//now process your SQL....
Azure_SQL _azuresql = new Azure_SQL();
_azuresql.writeMessageToStorage((_TwitterStreamFeed)_mqmsg.Body);
//refresh queue just in case any changes occurred (optional)
_mq.Refresh();
//tell MessageQueue to receive next message when it arrives
_mq.BeginReceive();
}
catch (Exception _ex)
{
throw;
}
dll 2
public void writeMessageToStorage(_TwitterStreamFeed _msmq_message_as_TSF)
{
try
{
// now do something with the class - i..e write the values to the database
SqlConnection _azurecon = new SqlConnection(_AzuzeSQLConnection);
SqlCommand _sqlcmd = new SqlCommand();
//Setup the command string to call the stored procedure
//Add the parameter to the parameters collection of the command
blah blah blah......... Do SQL writing to Db
_azurecon.Open();
SqlDataReader _sqldr_tweet_place = _sqlcmd_place.ExecuteReader(CommandBehavior.CloseConnection);
}
//now close things off
_azurecon.Close();
}
catch (Exception _ex)
{
// Throw the error to preserve the original
throw;
}
The reason for this is that, internally, the MessageQueue class is explicitly swallowing the exception. Where the MessageQueue class raises the ReceiveCompleted event, it's inside of a try-catch statement - and the catch block is empty. Suffice it to say, if an exception occurs inside your ReceiveCompleted event handler, _msgQ_RecieveCompleted(), nothing's ever going to know it happened.
I see a couple of options, in order of preference.
Option 1 - Shift where the asynchronous call is made
Since this exception-swallowing behavior only occurs when using BeginReceive(), in MSMQ_GetMessage(), you can switch from using BeginReceive() to just Receive(). Then, make your call to MSMQ_GetMessage() asynchronous and any exception that gets thrown will be propagated as expected.
As a side note, a new(er) alternative for making asynchronous calls is available; the Task<> class. As opposed to the Thread class, Task<> has exception handling functionality built in. It does, however, require Framework 4 or higher. There is a good explanation of it's use described in the answer here.
Option 2 - Use a custom event
If refactoring the asynchronous call isn't an option, you can create a custom event in your class in 'dll 2' and subscribe to that event in 'Console.exe'. So when an exception occurs in _msgQ_RecieveCompleted(), you can raise the event and 'Console.exe' will be notified.
The MessageQueue.BeginReceive() method uses the standard .NET APM (Asynchronous Programming Model) pattern. It is very important to understand how it works to know how to properly deal with exceptions. Be sure to read the MSDN article, there are lots of other googable resources available.
In APM, the callback that tells you that a message was received in executed on a thread-pool thread. Which is a very efficient way to get code to run quickly. It is however also a very troublesome way when something goes wrong. The EndReceive() method call is likely to throw an exception, it does so to tell you that the receive operation could not be completed. A standard exception it will throw is ObjectDisposedException. Which will happen when the MessageQueue object gets disposed. In your case when your program terminates. You need to catch that exception and exit from your event handler, it is an expected exception and signals that nothing more useful is going to happen next since the queue was closed.
Then there's a raft of possible exceptions that can be raised by major mishaps in the message queue plumbing. Plus whatever you do with the message. Looks like you execute some Azure code, plenty of ways that can fall over. If you let such an exception escape from the callback method, like you do, then there's no catch clause anywhere in the call stack that is going to handle the exception. The standard way .NET deals with unhandled exceptions is to raise the AppDomain.UnhandledException event and terminate your program. If you didn't actually implement that event then there's nothing decent to look at to diagnose the reason your program ended, the Windows Error Reporting dialog has no good diagnostic.
Whether or not you should try to handle the exception and prevent the program from terminating is up to you. But it pretty strongly fits the "don't shoot the messenger" pattern, it is very unlikely your program can meaningfully continue to execute when such an exception is raised. It invariably takes a human to fix the problem, like restoring the network connection or fixing the message queue. If you do catch it then the odds that the same exception is raised over and over again is fairly likely. After all, there wasn't anything decent you could do in your code to repair the network.
So the best guidance here is to not try, just make sure that IT staff has a good diagnostic so they can repair the problem. Do implement the AppDomain.UnhandledException and display and log the e.UnhandledException.ToString() value. This will also let you learn the number of ways that your program can fail. There might be some conditions that are common enough to warrant catching, something like a temporary network outage. At that point you'll also know what to do about it, in other words what kind of code to write in the catch clause. There is no possible way you know what to write right now, you should therefore not try.
Last but not least, do note that you got yourself into this pickle because you used BeginReceive() unnecessarily. You've already got a perfectly good thread to do work on. But it doesn't do anything useful, it is stuck in the Console.ReadKey() method. Particularly in .NET 4.5 a very tricky method to call, it prevents other threads from writing anything to the console. So your error reporting won't work, it will deadlock when it tries to use Console.WriteLine() to write a diagnostic.
You might as well use MessageQueue.Read() instead. Now dealing with exceptions is a lot easier since they occur on the same thread. The MessageQueue.SynchronizingObject can also be helpful to get completion callbacks to occur on the main thread, but that only works in a GUI app, not in a console app.

Categories