Hey C# folks,
I’m looking for some best practices on exception handling while maintaining some level of cleanliness –


Say I have code somewhat like below…. RunSQLQuery is either going to throw a SqlException “Operation cancelled by user.” or some other type. Where is it best practice to catch and display the errors to the user? Should I surround the caller in mainform with try/catch? Should I use try/catch around everything in the datareader as that’s where the errors start? Should I create a new error handling function in mainform and send it that way and return null out of RunSQLQuery?
Just looking for best practices before I get too much further as this method is called ~30x in an app I’m poking around on
Keep UI logic out of the database, and database logic out of the UI. You don’t want to put MessageBox code in the data layer. Creating projects with specific purposes helps a lot as when you see yourself needing to reference some UI assembly in the data layer, you’re probably doing something wrong.
If you really need to handle something directly at some point where you shouldn’t have access to the UI, (or some other context), consider raising an event, or exposing a Delegate or Action, and assigning something to handle it in the UI layer.
For WinForms, there is the AppDomain.CurrentDomain.UnhandledException event to catch, well, unhandled exceptions, but there may some caveats to it. There is also Application.ThreadException, make sure to read up on each and how they are used.
To avoid repetitive try...catch blocks consider writing a method that wraps around calls you expect to throw some set of exceptions. The method would take the delegate that calls the actual method you want to call.
It can even return a value.
As for exceptions in general,
Catch errors you expect and want to handle, and let everything bubble up as needed.
Sometimes you find the need to wrap an exception around your own, such as database exceptions that give too much information that you do not necessarily want to bubble up to, for example an API. Leverage the frameworks exception handling to handle exceptions at the top level, such as ASP.NET Core’s UseExceptionHandler to do logging and transformation. Let your Controller code worry about your business logic and let the framework handle everything else, as much as possible.
Put a try…catch if you want to suppress an exception and continue processing, if you really want to do that and your logic dictates you need to continue.
Catch specific exceptions. This is what Exception types are for, and why order of exceptions are important.


Don’t catch(Exception) as that will catch everything. Create Exception types judiciously, with the goal of narrowing down causes and capturing the correct context an exception was thrown in.
Fixed formatting.
Hello, rupertavery: code blocks using triple backticks (“`) don’t work on all versions of Reddit!
Some users see this / this instead.
To fix this, indent every line with 4 spaces instead.
FAQ
You can opt out by replying with backtickopt6 to this comment.
let everything bubble up as needed.
Not everything. Oftentimes, wrapping and rethrowing is essential for good error reporting. Typically, we want to add some context data as the exception unwinds. Exceptions thrown from the deep lack that. The context will be various function parameter or class member values.
On the other hand, it is often hard to know which ones are important and adding “everything” is impractical. So error reporting is a refinement process – as problems are reported, see the difficulty in understanding them and adjust reporting accordingly.
One approach could be adding a ‘var queryExceptions = new List<SqlException>();’ variable in the Mainform level of your program and pass that reference into RunSQLQuery() to queryExceptions.Add(err) any exceptions that are caught for later user display.. I would do this and add a try/catch on the Mainform level to catch any other unexpected exceptions
C# devs
null reference exceptions

source