In any Method, can you set anything that will automatically be picked up in the exception handling process?
The following will bubble up the ErrorMessage and the original Exception to the calling programs.
void Main()
{
string ErrorMessage = "FooBar";
try
{
throw new Exception();
}
catch (Exception ex)
{
throw new Exception(ErrorMessage, ex);
}
}
Is there any way to replicate this without a Try/Catch/Re-Throw?
Question restated:
Is there any way to have it (in any way) automatically pick up something I designate with the Exception?
void Main()
{
string ErrorMessage = "FooBar";
throw new Exception();
}
So I understand the concept of throwing a new exception with more information. I am interested in finding a way that when an exception occurs, it takes custom information ("FooBar") with it automatically, and it returns what the Try/Catch/Re-Throw accomplishes, without the Try/Catch and the Re-Throw.
** EDITED **
When the Exception naturally generated without re-throwing reaches the errorHandler, I'd like to be able to retrieve my ErrorMessage (say in ex.data?) which would have "FooBar" in it.
void ErrorHandler(Exception ex)
{
Log(ex.Data); // ex.Data is set to "FooBar";
}
"How would the exception system know which custom data you would like to have included in the exception?"
This is the basis of my question. What can I use that it CAN know? :P
Another thought to help express what I'm looking for:
public class CustomException : Exception
{
public CustomException() { }
public CustomException(string message) : base(message) { }
public CustomException(string message, Exception inner) : base(message, inner) { }
public string ErrorMessage { get; set;}
}
void Main()
{
CustomException cEx = new CustomException {ErrorMessage = "FooBar"};
throw new Exception(); // This would be any arbitrary error, not one I'm throwing...
}
Then in the ExceptionHandler event, perhaps override the creation of the new exception as in:
if (cEx != null)
{
// Use cEx in place of new Exception() as the error to fill to send
}
else
{
// Use new Exception() as the error to fill to send
}
"It seems to me that you might be confusing logging with exception management - it looks like the info you're wanting to gather automatically would normally be a part of your logging strategy."
OK, let's say you are right.
So, I don't want to log every time I come in, I just want to log the information when a random error occurs. Let's say I want to know the VALUES of any incoming Parameters as they came in and before they had a chance to change.
The StackTrace of the error will tell me the Method Name and the names of the Parameters and the TYPES of the Parameters. All good.
But I can find no way to GET the VALUES that were passed in, because one, they aren't available that I can find, and two, they may have changed since they were sent in.
Obviously I could ensure I don't change what came in, but that doesn't help if I can't get to the values anyhow.
Maybe solving this will solve my question in how we process errors.
So now the alternate question would be "How do I log the values of any incoming Parameters ONLY on an Exception?"
I was probably too specific in my question (I don't ask many questions here, I'm still learning the interaction here.) Perhaps I should have asked:
If I wanted to have an error message composed of:
"Error in myAssembly.GetStudentName(27)"
where 27 is an int (perhaps StudentID), how could I do it without Try/Catch/Rethrow?
I can create a string of
"Error in myAssembly.GetStudentName(27)"
coming in to every method. What I cannot do is get that string on any exception without a Try/Catch and do anything with it.
If I understand you correctly you want to avoid writing error handling logic everywhere.
I found the best way for me to solve this using lambda and wrapping this lambda with some generic logic.
Consider the following:
using System;
using System.Runtime.CompilerServices;
using static ConsoleApp.RuntimeUtility;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
try
{
Test();
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
public static void Test()
{
Run("Error", () =>
{
throw new Exception("Exception");
});
}
public static int Test2()
{
return Run("Error", () =>
{
throw new Exception("Exception");
return 0;
});
}
public static Task Test3()
{
return RunAsync("Error", async () =>
{
await Task.Delay(100);
throw new Exception("Exception");
});
}
public static Task<int> Test4()
{
return RunAsync("Error", async () =>
{
await Task.Delay(100);
throw new Exception("Exception");
return 1;
});
}
}
public class RuntimeUtility
{
public static void Run(string errorMessage, Action action, [CallerMemberName] string name = null)
{
try
{
action();
}
catch (Exception e)
{
throw new Exception(errorMessage + " from method " + name, e);
}
}
public static T Run<T>(string errorMessage, Func<T> action, [CallerMemberName] string name = null)
{
try
{
return action();
}
catch (Exception e)
{
throw new Exception(errorMessage + " from method " + name, e);
}
}
public static async Task RunAsync(string errorMessage, Func<Task> action, [CallerMemberName] string name = null)
{
try
{
await action();
}
catch (Exception e)
{
throw new Exception(errorMessage + " from method " + name, e);
}
}
public static async Task<T> RunAsync<T>(string errorMessage, Func<Task<T>> action, [CallerMemberName] string name = null)
{
try
{
return await action();
}
catch (Exception e)
{
throw new Exception(errorMessage + " from method " + name, e);
}
}
}
}
I have the logging on the very top level, and everywhere else I use the Run method which wraps the lambda with exception handling logic, and I can pass any data to the Run method so that it can be used in case of exception.
And there is also couple of things which allow to write less code - [CallerMemberName] attribute which will have caller method name; also using static
.
Upd: Added examples for returning value and for async/await.