This is not so much of a problem but more feedback and thoughts. I have been considering an implementation for methods that have been tested thoroughly through our internal teams. I would like to write a generic exception catch method and reporting service.
I relize this is not as easy as a “try-catch” block, but allows for a uniform method for catching exceptions. Ideally I would like to execute a method, provide a failure callback and log all the parameters from the calling method.
Generic Try-Execute.
public class ExceptionHelper
{
public static T TryExecute<T, TArgs>(Func<TArgs, T> Method, Func<TArgs, T> FailureCallBack, TArgs Args)
{
try
{
return Method(Args);
}
catch (Exception ex)
{
StackTrace stackTrace = new StackTrace();
string method = "Unknown Method";
if (stackTrace != null && stackTrace.FrameCount > 0)
{
var methodInfo = stackTrace.GetFrame(1).GetMethod();
if (methodInfo != null)
method = string.Join(".", methodInfo.ReflectedType.Namespace, methodInfo.ReflectedType.Name, methodInfo.Name);
}
List<string> aStr = new List<string>();
foreach (var prop in typeof(TArgs).GetProperties().Where(x => x.CanRead && x.CanWrite))
{
object propVal = null;
try
{
propVal = prop.GetValue(Args, null);
}
catch
{
propVal = string.Empty;
}
aStr.Add(string.Format("{0}:{1}", prop.Name, propVal.ToString()));
}
string failureString = string.Format("The method '{0}' failed. {1}", method, string.Join(", ", aStr));
//TODO: Log To Internal error system
try
{
return FailureCallBack(Args);
}
catch
{
return default(T);
}
}
}
}
What I know as draw backs.
- Performance Loss using reflection
- MethodBase (methodInfo) may not be available through optimization
- The try-catch around the error handler. Basically I could use the TryExecute wrapper for the try-catch around the error call back however that could result in a stack overflow situation.
Here would be a sample implementation
var model = new { ModelA = "A", ModelB = "B" };
return ExceptionHelper.TryExecute((Model) =>
{
throw new Exception("Testing exception handler");
},
(Model) =>
{
return false;
},
model);
Thoughts and comments appreciated.
That’s a lot of code to put in a
catch, including two moretry/catchblocks. Seems like a bit of overkill if you ask me, with a good amount of risk that a further exception can obscure the actual exception and that the error information would be lost.Also, why
return default(T)? Returning defaults or nulls as indications of a problem is usually pretty sloppy. If nothing else, it requires the same conditional to be wrapped around every call to the method to check for the return and respond to… some error that has gone somewhere else now.Honestly, that usage example looks pretty messy, too. It looks like you’ll end up obscuring the actual business logic with the error-trapping code. The entire codebase will look like a series of error traps, with actual business logic hidden somewhere in the entanglement of it. This takes valuable focus off of the actual intent of the application and puts something of background infrastructure importance (logging) at the forefront.
Simplify.
If an exception occurs within a method, you generally have two sensible options:
There’s absolutely nothing wrong with an exception escaping the scope of the method in which it occurs. Indeed, exceptions are designed to do exactly that, carrying with them useful stack information about what happened and where. (And, if you add meaningful runtime context to the exception, it can also carry information about why.)
In fact, the compiler even subtly hints at this. Take these two methods for example:
One of these methods will compile, one of them will not. The compiler error will state that not all code paths return a value on the former method. But why not the latter? Because throwing an exception is a perfectly acceptable exit strategy for a method. It’s how the method gives up on what it’s doing (the one thing it should be trying to do and nothing more) and let’s the calling code deal with the problem.
The code should read in a way that clearly expresses the business concept being modeled. Error handling is an important infrastructure concept, but it’s just that… infrastructure. The code should practically scream the business concept being modeled, clearly and succinctly. Infrastructure concerns shouldn’t get in the way of that.