Multiple Exceptions have occurred, what's the right way to rethrow? - c#

public void DoRiskyThings(List<Action> tasks)
{
List<Exception> exceptions = new List<Exception>();
foreach(Action task in tasks)
{
try {task()}
catch (Exception e) {exceptions.Add(e)};
}
if (exceptions.Any())
{
//... what goes here?
}
}
I'd like to preserve all the information (especially messages and stacktraces).

You're looking for the AggregateException class.

Just bundle your list into a super-exception:
class MultiException : Exception
{
public List<Exception> ExceptionsList { get; set; }
}

Related

Strange behavior of exception propagation

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) { }
}
}

How to get the values from inside the code when exception occurs?

I have a worker service in .net core 3.1
in my Program.cs i have the below codes
public static void Main(string[] args)
{
try
{
CreateHostBuilder(args).Build().Run();
}
catch(Exception ex)
{
Handler(ex);
}
}
static void Handler( Exception e)
{
var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
List<Test> _Test = new List<Test>()
{
new Test()
{
}
};
LogEventInfo eventInfo = new LogEventInfo
{
Level = LogLevel.Error,
Properties = { { "Application",_Test } }
};
logger.Log(eventInfo);
}
private class Test
{
public string Name{get;set;}
public string Place{get;set;}
}
In my worker class i have code as below
public class Worker : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
do
{
string Name ="MyName";// These values will be fetched from different file
string Place="MyPlace";
//Some Logic where an exception may occur
}
while (!stoppingToken.IsCancellationRequested);
}
}
Is there anyway to get the values of Name and Place of worker class to Handler method in program class when an exception arises. Since I'm thinking of a global exception handler I'm thinking of not putting any more try catch blocks. I want to handle all the exception with the try catch in the program.cs file. How can i get the Name and Place values onto my handler on such scenario so that it can be logged?
While a custom exception is a possibility, you could also simply decorate any exception thrown inside your service with those properties using the Data property:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
do
{
string Name = "MyName";
string Place = "MyPlace";
try
{
//Some Logic where an exception may occur
}
catch (Exception e)
{
e.Data["Name"] = Name;
e.Data["Place"] = Place;
throw;
}
}
while (!stoppingToken.IsCancellationRequested);
}
Create a custom Exception class where you can set Name & Place as properties.
In the Worker, add a try catch block around the code that may throw an exception. Create and throw your custom exception, setting the original exception as the InnerException (https://learn.microsoft.com/en-us/dotnet/api/system.exception.innerexception?view=net-6.0)
Then in your handler get the name / place from the wrapper exception, and then use the InnerException for the rest.

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

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();
}

Modify exception data while logging with NLog

I have a global try/catch around my application methods to log unhandled exceptions and show an error message. For logging I use NLog. If I handle an exception and log it manually I set a key 'logged' in the exceptions data object to prevent the second log from the global try/catch.
public partial class App : Application
{
public static void Main(string[] args)
{
try
{
// Application stuff
SomeMethod();
}
catch (Exception ex)
{
if (ex.Data['logged'] == false)
{
logger.Error(ex, "A unhandled exception occured.")
ex.Data['logged'] = true;
}
// Show error dialog
}
}
public static void SomeMethod()
{
try
{
// Method stuff
}
catch (Exception ex)
{
logger.Error(ex, "A handled exception occured.")
ex.Data['logged'] = true;
throw;
}
}
}
I want to simplify the process so that I don't have to call ex.Data['logged'] = true; manually. Is there a way to modify the data property of an exception automatically while logging with NLog? I tried to use a MethodCall-Target but I had no access to the exception reference. The exception could only be passed as a string.
Would it work for you to use NLog to perhaps modify your approach to logging exceptions slightly?
public partial class App
{
private static Logger logger = NLog.LogManager.GetCurrentClassLogger();
public static void Main(string[] args)
{
try
{
SomeMethod();
}
catch (Exception ex)
{
logger.ErrorEx(ex, "message");
}
}
public static void SomeMethod()
{
try
{
// Method stuff
}
catch (Exception ex)
{
logger.ErrorEx(ex, "A handled exception occured.");
throw;
}
}
}
public static class NLogExt
{
public static void ErrorEx(this NLog.Logger logger, Exception ex, string message)
{
if (ex.Data["logged"] as bool? == true) return;
logger.Error(ex, message);
ex.Data.Add("logged", true);
}
}

Categories