Strange behavior of exception propagation - c#

This is an example from https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/exception-handling-task-parallel-library
I have tried to execute it in Visual Studio 2022, C#10, Net 6 but exception is not propagated to try catch block.
We can emulate some delay to force it to propagate (via sleep, or task.Wait(10), ...) but this is not documented.
Is this some kind of compiler optimization or is it bug?
public class Program
{
public static void Main()
{
var task = Task.Run(
() => {
//Thread.Sleep(10); //if uncomment then exception is propagated
throw new CustomException("This exception is expected!");
});
try
{
task.Wait(); //we can use task.Wait(1) then exception is caught
}
catch (AggregateException ae)
{
foreach (var ex in ae.InnerExceptions)
{
// Handle the custom exception.
if (ex is CustomException)
{
Console.WriteLine(ex.Message);
}
// Rethrow any other exception.
else
{
throw ex;
}
}
}
}
class CustomException : Exception
{
public CustomException(string s) : base(s) { }
}
}

Related

Exception not being caught by first catch, and instead get handled by a top level catch

I have a class called SearchProbe for I'm writing unit tests. One unit test is for testing the ability of my class's main processing method (called RunSearchProbe) to be able to respond to CancellationTokens correctly. My class's main processing method executes async submethods which all throw an OperationCanceledException when a CancellationToken is cancelled. Then in my main method RunSearchProbe, I'm trying to catch this exception and respond to it.
Problem: The problem is that for some reason, OperationCanceledException is NOT being caught in the main method RunSearchProbe, and it comes all the way upto my unit test's call stack for handling, and I don't know why ?!
Here's my main class:
public class SearchProbe
{
protected async Task RunSearchProbe(CancellationToken cancellationToken) {
try
{
try
{
using (cancellationToken.Register(() => {
//some code here
}))
{
Task<bool> initTask = Initialize(cancellationToken);
await initTask;
//some code here
}
}
catch (Exception exception) when (exception.GetType().Equals(typeof(OperationCanceledException))
|| exception.InnerException.GetType().Equals(typeof(OperationCanceledException)))
{
//some code here // -------->>> (Point 1) This is where the OperationCanceledException SHOULD get caught
}
finally
{
//some code here
}
}
catch (Exception e)
{
//some code here // -------->>> (Point 2) ... Or AT LEAST get caught here
}
}
private async Task<bool> Initialize(CancellationToken cancellationToken) {
try
{
using (cancellationToken.Register(() => {
throw new OperationCanceledException();
}))
{
//some code here
return true;
}
}
catch (Exception exception)
{
//some code here
}
}
}
This is a mock inherited class:
class MockSearchProbe : SearchProbe
{
static MockSearchProbe()
{
//some code here
}
public async Task RunProbeManually()
{
try {
CancellationTokenSource cts = new CancellationTokenSource();
Task probeTask = RunSearchProbe(cts.Token);
cts.Cancel();
await probeTask;
}
catch (Exception exception) when (exception.GetType().Equals(typeof(OperationCanceledException))
|| exception.InnerException.GetType().Equals(typeof(OperationCanceledException)))
{
//do something (Point 3) ... But it actually gets caught here for some reason
}
}
}
This is the test class:
[TestClass]
public class SearchProbeTests
{
[TestMethod]
public async Task TestProbe_Cancellation()
{
MockSearchProbe probe = new MockSearchProbe();
Task result = probe.RunProbeManually();
await result;
}
}
Please see steps 1, 2 and 3 commented above to see what I mean ... Why is the catch block inside my main class's RunSearchProbe method NOT catching the OperationCanceledException ??
The documentation for CancellationToken.Regsiter states that the method:
Registers a delegate that will be called when this CancellationToken is canceled.
Based on that description, I would expect that the registration callback defined in the Initialize method should execute when cts.Cancel() is called in RunProbeManually. The exception is not instantiated or thrown until that point, which is in the scope of the try/catch block labeled "Point 3."
Here's a simplified illustration:
using System;
class MainClass {
public static void Main (string[] args) {
Action throwException = null;
try {
Console.WriteLine("Defining delegate");
throwException = () => {
Console.WriteLine("Throwing exception");
throw new Exception();
};
} catch (Exception) {
Console.WriteLine("Exception caught at point 1");
}
try {
Console.WriteLine("Invoking delegate");
throwException.Invoke();
} catch (Exception) {
Console.WriteLine ("Exception caught at point 2");
}
}
}
Output:
Defining delegate
Invoking delegate
Throwing exception
Exception caught at point 2

Rethrowing inner exception of an AggregateException

Let's say I have an Interface:
interface A {
string Do();
}
and then I implement this interface in a class. The implementation requires some async operations. Something like the following:
class B : A {
public string Do() {
return Task1().Result;
}
private async Task<string> Task1() {
var str = await Task2();
return str + "task1";
}
private async Task<string> Task2() {
using (WebClient client = new WebClient())
{
return System.Text.Encoding.UTF8.GetString(await client.DownloadDataTaskAsync(new Uri("http://test.com")));
}
}
}
What is the proper way to return, to the external calling code, the first exception that occurs in the async operations chain? Is the following a good approach?
public string Do() {
try {
return Task1().Result;
} catch (AggregateException ex) {
Exception inner = ex;
while(inner.InnerException != null) {
inner = inner.InnerException;
}
throw inner;
}
}
From your code, through the while, I think you want to throw the first exception in AggregateException
To do that, you can use Flatten
Flattens an AggregateException instances into a single, new instance.
It helps to put the exceptions in "the same hierarchy", you can then simply call FirstOrDefault to get the first exception.
Supposed this code:
Task.Factory.StartNew(
async () =>
{
await Task.Factory.StartNew(
() => { throw new Exception("inner"); },
TaskCreationOptions.AttachedToParent);
throw new Exception("outer");
}).Wait();
}
The stucture of exceptions likes
AggregateException
Exception: outer
AggregateException
Exception: inner
With Flatten, I can get inner
catch(AggregateException ex)
{
Console.WriteLine(ex.Flatten().InnerExceptions.FirstOrDefault().Message);
}
but without Flatten, I get AggregateException, which isn't correct
catch(AggregateException ex)
{
Console.WriteLine(ex.Flatten().InnerExceptions.FirstOrDefault().Message);
}
With your case, this line can help you get the first exception
ex.Flatten().InnerExceptions.FirstOrDefault().Message
You have also the method Handle, which help you handle the exception inside AggregateException
catch (AggregateException ex)
{
ex.Handle(x =>
{
if (x is UnauthorizedAccessException)
{
//the exception you interested
throw x;
}
// Other exceptions will not be handled here.
//some action i.e log
return false;
});
}

Handling exception in task

I'm new to TPL.
I need to handle exception when the SendEmailAlert() method throws any error.Is the following code correct please?
public Task MyMethod()
{
DoSomething();
try
{
string emailBody = "TestBody";
string emailSubject = "TestSubject";
Task.Run(()=> SendEmailAlert(arrEmailInfo));
}
catch (AggregateException ex)
{
ex.Handle((e) =>
{
log.Error("Error occured while sending email...", e);
return true;
}
);
}
}
private void SendEmailAlert(string[] arrEmailInfo)
{
MyClassX.SendAlert(arrEmailnfo[0], arrEmailnfo[1]);
}
I forced an error from within SendEmailAlert() method.But the exception is not getting caught. Could someone advise?
Thanks.
Your Task.Run runs in a different context (you would need a try/catch inside it; or check if the task is done). You could change to use async/await.
Example:
public async void MyMethod()
{
try
{
await ExceptionMethod();
}
catch (Exception ex)
{
// got it
}
}
public async Task ExceptionMethod()
{
throw new Exception();
}

How to handle thrown exception in NUnit

I have written a unit test class in C# for my MVC project.
The Test Method is following
[Test]
public void To_Add_DocumentStatusIsNull_ThrowsInvalidOperationException_ServiceTest()
{
try
{
_IDocumentStatusRepositoryMock = new Mock<IDocumentStatusRepository>();
_unitOfWorkMock = new Mock<IUnitOfWork>();
DocumentStatusService documentStatusService = new
DocumentStatusService(_unitOfWorkMock.Object,
_IDocumentStatusRepositoryMock.Object);
DocumentStatus documentStatus;
documentStatus = null;
_IDocumentStatusRepositoryMock.Setup(m => m.Add(documentStatus));
documentStatusService.Add(documentStatus);
Assert.Pass();
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
And the Service Method is following
public virtual void Add(TEntity entity)
{
try
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_repository.Add(entity);
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
Now This test method only not passed due to the service class thrown ArgumentNullException.So how to handle the ArgumentNullException or How to make this test pass?
Please anybody help
If you are trying to check that the ArgumentNullException is working (which: it isn't currently). then it sounds like you want:
[Test, ExpectedException(typeof(ArgumentNullException), ExpectedMessage = #"Value cannot be null.
Parameter name: entity")]
public void To_Add_DocumentStatusIsNull_ThrowsInvalidOperationException_ServiceTest()
{
_IDocumentStatusRepositoryMock = new Mock<IDocumentStatusRepository>();
_unitOfWorkMock = new Mock<IUnitOfWork>();
DocumentStatusService documentStatusService = new
DocumentStatusService(_unitOfWorkMock.Object,
_IDocumentStatusRepositoryMock.Object);
DocumentStatus documentStatus;
documentStatus = null;
_IDocumentStatusRepositoryMock.Setup(m => m.Add(documentStatus));
documentStatusService.Add(documentStatus);
}
...
public virtual void Add(TEntity entity)
{
if (entity == null)
{
throw new ArgumentNullException("entity");
}
_repository.Add(entity);
}
Testing for the ArgumentNullException
If you remove the ill-advised
catch (Exception e)
{
throw new Exception(e.Message);
}
from your code to be tested (The current catch loses context of the error, and breaks the stack trace, see below), your test can be as simple as wrapping the invocation in an Assert.Throws<ArgumentNullException>():
[Test]
public void PassingANullEntityToAddMustThrowArgumentNullException()
{
var documentStatusService = new DocumentStatusService(...);
Assert.Throws<ArgumentNullException>(() => documentStatusService.Add(null));
}
Re: Your Exception Handler
In your service code, never catch an exception and rethrow it as you've done, as this will lose the stack trace (e.g. _repository.Add(entity); could throw as well.). You also aren't adding any value by throwing e.Message as this is already in the original exception (with additional info like stack trace and inner exception)
Bad:
catch (Exception e)
{
throw new Exception(e.Message);
}
Better: If you do catch and rethrow with some value, wrap the original as an inner exception:
catch (SqlException ex)
{
throw new Exception("Some value add here", ex);
}
or if you are just intercepting and allow to propagate:
catch (SqlException)
{
// Do some logging
throw;
}
Best to me would to let the exception propagate, unless you either adding value, or handling it.
I am assuming: Looking at the code this unit test should not pass. Adding a NULL to a list is in most cases not an intended behaviour.
I see 2 options:
A) You should add a try/catch to you Test metod.
try
{
_IDocumentStatusRepositoryMock.Setup(m => m.Add(documentStatus));
documentStatusService.Add(documentStatus);
}
catch (Exception )
{
Assert.Fail(); // or nothing is expected behaviour
}
B) Remove the try/catch block from the Test Method so you do not swallow the exception. (Every Test that does not fails or an Assert or thows an unhandeled exception automatically passes)

What is the best practice in C# to propagate an exception thrown in a finally block without losing an exception from a catch block?

When an exception is possible to be thrown in a finally block how to propagate both exceptions - from catch and from finally?
As a possible solution - using an AggregateException:
internal class MyClass
{
public void Do()
{
Exception exception = null;
try
{
//example of an error occured in main logic
throw new InvalidOperationException();
}
catch (Exception e)
{
exception = e;
throw;
}
finally
{
try
{
//example of an error occured in finally
throw new AccessViolationException();
}
catch (Exception e)
{
if (exception != null)
throw new AggregateException(exception, e);
throw;
}
}
}
}
These exceptions can be handled like in following snippet:
private static void Main(string[] args)
{
try
{
new MyClass().Do();
}
catch (AggregateException e)
{
foreach (var innerException in e.InnerExceptions)
Console.Out.WriteLine("---- Error: {0}", innerException);
}
catch (Exception e)
{
Console.Out.WriteLine("---- Error: {0}", e);
}
Console.ReadKey();
}
I regularly come into the same situation and have not found a better solution yet. But I think the solution suggested by the OP is eligible.
Here's a slight modification of the original example:
internal class MyClass
{
public void Do()
{
bool success = false;
Exception exception = null;
try
{
//calling a service that can throw an exception
service.Call();
success = true;
}
catch (Exception e)
{
exception = e;
throw;
}
finally
{
try
{
//reporting the result to another service that also can throw an exception
reportingService.Call(success);
}
catch (Exception e)
{
if (exception != null)
throw new AggregateException(exception, e);
throw;
}
}
}
}
IMHO it will be fatal to ignore one or the other exception here.
Another example: Imagin a test system that calibrates a device (DUT) and therefore has to control another device that sends signals to the DUT.
internal class MyClass
{
public void Do()
{
Exception exception = null;
try
{
//perform a measurement on the DUT
signalSource.SetOutput(on);
DUT.RunMeasurement();
}
catch (Exception e)
{
exception = e;
throw;
}
finally
{
try
{
//both devices have to be set to a valid state at end of the procedure, independent of if any exception occurred
signalSource.SetOutput(off);
DUT.Reset();
}
catch (Exception e)
{
if (exception != null)
throw new AggregateException(exception, e);
throw;
}
}
}
}
In this example, it is important that all devices are set to a valid state after the procedure. But both devices also can throw exceptions in the finally block that must not get lost or ignored.
Regarding the complexity in the caller, I do not see any problem there either. When using System.Threading.Tasks the WaitAll() method, for example, can also throw AgregateExceptions that have to be handled in the same way.
One more note regarding #damien's comment: The exception is only caught to wrap it into the AggregateException, in case that the finally block throws. Nothing else is done with the exception nor is it handled in any way.
For those who want to go this way you can use a little helper class I created recently:
public static class SafeExecute
{
public static void Invoke(Action tryBlock, Action finallyBlock, Action onSuccess = null, Action<Exception> onError = null)
{
Exception tryBlockException = null;
try
{
tryBlock?.Invoke();
}
catch (Exception ex)
{
tryBlockException = ex;
throw;
}
finally
{
try
{
finallyBlock?.Invoke();
onSuccess?.Invoke();
}
catch (Exception finallyBlockException)
{
onError?.Invoke(finallyBlockException);
// don't override the original exception! Thus throwing a new AggregateException containing both exceptions.
if (tryBlockException != null)
throw new AggregateException(tryBlockException, finallyBlockException);
// otherwise re-throw the exception from the finally block.
throw;
}
}
}
}
and use it like this:
public void ExecuteMeasurement(CancellationToken cancelToken)
{
SafeExecute.Invoke(
() => DUT.ExecuteMeasurement(cancelToken),
() =>
{
Logger.Write(TraceEventType.Verbose, "Save measurement results to database...");
_Db.SaveChanges();
},
() => TraceLog.Write(TraceEventType.Verbose, "Done"));
}
As the comments have suggested this may indicate "unfortunately" structured code. For example if you find yourself in this situation often it might indicate that you are trying to do too much within your method. You only want to throw and exception if there is nothing else you can do (your code is 'stuck' with a problem you can't program around. You only want to catch an exception if there is a reasonable expectation you can do something useful. There is an OutOfMemoryException in the framework but you will seldom see people trying to catch it, because for the most part it means you're boned :-)
If the exception in the finally block is a direct result of the exception in the try block, returning that exception just complicates or obscures the real problem, making it harder to resolve. In the rare case where there is a validate reason for returning such as exception then using the AggregateException would be the way to do it. But before taking that approach ask yourself if it's possible to separate the exceptions into separate methods where a single exception can be returned and handled (separately).

Categories