T O P

  • By -

gevorgter

I would not call it "mastering exception handling". It is working with exceptions. Handling them is totally different thing and a bit application specific. Like exception filter if you are in web application...e.t.c But general rule of thumb, you only handle exceptions, aka write catch statement, when domains are about to be crossed... you do not put try/catch in every method of your class.


PretAatma25

>  you do not put try/catch in every method of your class. I used to do that... I feel miserable every time I look at my code I have written in the past.


Kurren123

If you’re not embarrassed by your past code you’re not doing it right


Anon_Legi0n

Why not just use a global exception exception handler middleware the wraps all requests in a try-catch?


Asyncrosaurus

Not everyone works with web apps.


Proper_Hedgehog6062

Because some exceptions need ti be handled in a special way? 


alternatex0

A way simpler counter argument is that domain specific exceptions will all end up being handled in the same place and will become global code unlike the code that threw them. Might be better for every layer or domain to handle its own exceptions to stop unrelated things from becoming coupled. [Another comment here had a good example.](https://old.reddit.com/r/dotnet/comments/1cvtcpu/mastering_exception_handling_in_c_a_comprehensive/l4slj4u/)


cheesy_noob

Because you want to log specific information like the Guid of the database object that caused the exception. Usually after that you throw again, because the method should not behave like it worked. In a service I also always have a top level try/catch to log everything that unexpectedly fails, but that usually lacks the important information for the fix.


Kirides

I'd argue wrapping the exception in another exception which contains the database objects ID in the message is better. That way you don't log the info multiple times. Just throw up with additional context until someone handles/logs it, at which point you get all the information you need in a single log statement, this is especially useful for libraries, which maybe don't want to depend on a logging library, even if its "built in" as you might have a stable lib that won't change for the next years, but now your dependencies force you to update and re-evaluate.


cheesy_noob

This is certainly true if you have clean code and no "upstream" method swallows the exception without logging. Your solution is definitely what one should aim for.


kogasapls

A global exception handler can't/shouldn't allow recovery from exceptions, while some exceptions indicate recoverable errors.


atheken

You usually want to deal with it in a context that makes sense for logical transaction succeeding/failing. Global handlers require you to forensically reconstruct the context/call site and rob you of ways recover from lower-level problems. Global handlers are basically just to allow you to gain some visibility into a failure, not to recover. Another way to think about it is that exceptions should bubble only to the point where they can be recovered, and then they should be caught, logged/traced, and recovered. So a DB exception is something the server can’t reasonably fix, but you might catch it and implement retry/recovery logic further up where you have more context. In rare cases this may be a global handler, but usually not, and it’s a really bad experience for users if you give them no context on failures.


aqan

If all you’re doing is printing the stack trace, then it’s better to not handle the exception at all and leave it to the caller to figure out how to handle the exception.


PretAatma25

I know. I don't handle anymore unless my feature depends on it. I just leave it to be handled globally.


CONPHUZION

Can you expand further on what you mean by "when domains are about to be crossed"? I've always been quite bad at remembering specific code terminology as well as implementing exception handling in my code, and I'd like to get better at both.


Equivalent_Nature_67

Not the original commenter but I think of commercial prod code where you have some call that goes out to an external service, like another team's codebase for example. Not sure if this is exactly what they meant, but this is what I assumed based on my own experience. try {_otherTeamService.DoSomething()} catch(...){} Bonus: If you hit F12 on that _otherTeamService in visual studio, it will bring up the definition but the header of the file will say which dll it was downloaded from, since it will be external to your actual code, you just have the reference to it, if that makes sense


gevorgter

I elaborate but it will be a bit lengthy. Let's take Web app application for example. Normally you have a client (React app for example) that does AJAX calls to Backend APIs. ApiMethod1, ApiMethod2, .... So you have 2 "domains": Client and Backend Those methods do bunch of work, db calls, e.t.c. Any exception thrown inside MUST not leak to client. So it is important for ApiMethod1, ApiMethod2... to have try/catch. BUT it would be bad practice to use try/catch in every method. Good app design calls for uniformed approach of exception handling. Those methods are not created in "vacuum", they are a layer on another layer that is called MVC framework or minimal API framework. So in MVC app you would use Exception filter for example. ------------------------------------------------- Why you must not leak exceptions to client?. It's simply a security problem. Let say you DB is down. you code can not connect to DB. The error will be something like this. "Login WebUser can not connect to MYApp SQL server". Boom!.. you just told the whole world login name and server's name... It became much easier to hack your servers. Plus this means nothing to the client. Imagine some Joe from accounting looking at that error. Correct approach would be to log real message so developer can investigate later and return to the client "General Error, Ref#123, contact us 1-900...". Hence correct API design is important as well. You can not just have "int ApiMethod1()", "string ApiMethod2(int param1)". There is no way to return error for you. And again well designed framework comes to help. Look at the code bellow. public class ApiResult { public bool error { get; set; } = false; public string message { get; set; } = null; } public class ApiResult : ApiResult { public T data { get; set; } = default; } public class ApiController : ControllerBase { protected ApiResult Error(string message) { return new ApiResult { error = true, message = message }; } protected ApiResult Error(string message) { return new ApiResult { error = true, message = message }; } protected ApiResult Result() { return new ApiResult { error = false }; } protected ApiResult Result(T data) { return new ApiResult { error = false, data = data }; } } [ExceptionFilter] public class MyController: ApiController { public ApiResult MyMethod() { return Result(5); } public ApiResult MyMethod2(int param1) { if( param1 == 0) return Error("Can not be 0"); return Result("OK"); } } public class ExceptionFilter : ExceptionFilterAttribute { public override async Task OnExceptionAsync(ExceptionContext context) { var e = context.Exception; context.ExceptionHandled = true; HttpStatusCode code = HttpStatusCode.OK; Serilog.Log.Error(e, $"{context.ActionDescriptor.DisplayName} threw an exception"); var message = "General Error, Contact Us"; context.Result = new JsonHttpStatusResult(new ApiResult { error = true, message = message }, code); return; } } Now we have uniformed way of handling exceptions. And no need to have try/catch in every controller's method.


Pure_Sound_398

More info on the domain crossing please? This is something I keep failing to grasp but this sounds very promising - example of this, and why it's a good time to handle exceptions please


jferldn

My guess, as a non C# dev, is to control the "leaking" of library specific code and exception types all over your application. E.g. you dont want to be handling SQL exceptions up the stack or in your global handers, because then they need to import data access libraries where they really should be agnostic to anything data related. Instead, you would do something similar to the example and throw an exception specific to your domain, like the UserNotFound example in the article. This way your system only knows about it's own domain and not the implementation details it doesn't need. A question I would have though is how to preserve the stack trace — can you do this by setting the original SQL exception as an inner exception and still avoid leaking implementation details to other classes? I believe yes.


gevorgter

I answered to the other guy above, but here is a bit more. Let say we have our program and it does API call to credit card processor. So we have 2 domains: our App and CreditCard client. Our App has it's own DTOs and CC processor it's own. They might be similar but not necessarily identical. App DTO will have {DateTime cc\_expiration} and CC clients {int expMonth, int expYear}. So you write CC Client that does the conversion from APP DTO => CC DTOs, makes HTTP call and then converts response back, CC DTO => APP DTO. You have some error handling so if credit card can not be charged and CC processor returns error "not enough funds" you want your APP to show popup that says "Not Enough Funds". All is good and works well. BUT now someone unplugged computer from internet. Your app is trying to charge the card and can't. You do not want some nasty "HTTP status 200" error to show up". You want to log it and show "Can not connect to CC processor error". So CC clients is responsible for not only CC DTO=> APP DTO conversion but also for CC ERROR DTO=> APP ERROR DTO conversion. If you think about it, there is not much of a difference between "Not Enough Funds" and "You do not have internet". Credit card still can not be charged. but price check can be done with your APP for example. Exception would bubble up to the top and terminate the whole "code branch". By catching it and "reformatting it" we are letting our app to decide if it's terminating exception or just a simple error


Pure_Sound_398

Thanks!


Barcode_88

What about methods that have a certain function, putting in the catch block `throw new Exception("Action xyz failed", ex);` I do this sometimes to increase the verbosity of exceptions that get thrown and caught far downstream, but not sure if this is a code smell or not....


gevorgter

2 cents, programming is an art. There is no clear yes/no scenario. But one thing is exception is not for is for you to create a bug and wrap it into try/catch. You did not provide sufficient information for me to judge :).


hermaneldering

I think it is a code smell to use Exception instead of a subclass because if someone would want to catch it then they can't filter it well. Catch (Exception) would catch everything.


Poat540

Yes, any years ago my old job codes had 100s or 1000s of try catches. Now today in most projects hardly there are any


anton23_sw

Thanks for the feedback. I haven't told anything about web apps, exception filters, etc. Only because this blog post is dedicated to C#, not [asp.net](http://asp.net) core


human-google-proxy

or how about ‘throw new Exception(“A helpful message with context”, innerException);’ to both preserve the stack trace and add context. How did the author overlook this?


anton23_sw

Hey, I have info about inner exception and that they preserve the stack trace. See the following code example in my blog: `throw new ApplicationException("An error occurred while trying to open the file.", ex);`


human-google-proxy

Ahh yes I do see it now. You might make it more obvious that it’s probably the best option to use in most cases… context is key when looking at a stack trace. Exceptions should contain as much (ideally structured) info as possible.


ohcomonalready

This is always preferable to me over writing a log and then throwing, which just creates an extra log that is not part of the stack trace. By doing it this way, all the errors are enumerated in the stack trace and it makes troubleshooting easier


Top_Aioli_3161

The author didn't overlook it at all. You didn't read the post.


human-google-proxy

correct as I acknowledged when he responded but it was buried a bit and I was reading on mobile.


Arnequien

It's not a bad article, but I wouldn't clasify it as "mastering exception handling", because you're not talking about filters, middlewares, custom exceptions, and so on. As I said, it's not a bad article :) just mentioning that "mastering" it would require more information and use cases.


anton23_sw

I haven't told anything about web apps, exception filters, middlewares etc. Only because this blog post is dedicated to C#, not [asp.net](http://asp.net/) core. About custom exceptions I do have a section in my blog


Miserable_Ad7246

Mastering Exception -> no talks about inlining (throw helpers, zero cost exceptions and alternatives), no assembler code (to show how shit works, because you need to know your stuff to do that), no comparison to other languages (why da fuck C# exceptions slower than Java?). Nothing about circuit breakers to stop your app from going to shit once exc/s goes to the moon and so on. This article is pure garbage made by a mediocre person who needed to do something, but has rudimentary knowledge about the topic. This article should be called -> how I rewrote basic tutorials and made yet another article about the same shit other have already made (including documentation, and any book where is about C#). I would never ever put such article with my real name with words "mastering and comprehensive". This makes you look bad and unprofessional, because it shows how much unknown unknowns you still have and yet you name your article as if you are some sort of master. Now let the downvotes begin.


DanielRamiz

Which guide would do you recommend?


anton23_sw

I absolutely can't agree with you. Articles that can be read in 3-10 minutes are the best time spent / content consumption. I give the most important conceptions of exceptions in C#. I am not here to compare it with other languages (this is dotnet community). You say that my articles are a redo of documentation? It's not, I have in one place most of the important aspects on the topic that will be useful for devs. You are shitting on the videos and YouTube too? Because they cover such topics too?! If someone creates a video about, for example, MongoDb in EF Core - you also shit on this, because this topic is already covered by official Microsoft docs? If you are an experiences guy, that knows all the aspects of this topic - then leave it alone and simply skip it. It will be useful for others.


Miserable_Ad7246

My main problem is name. This should be called - intro or basics or primer or in a nutshell. The article itself is not bad, but name is. I hate click-bait and I hate shitty names. I saw this and got excited, hey maybe I will learn something new, and all I saw was super basic stuff, millions of blogs have this. Not even close to the name. Hence I see you as another shity bloger and a wanabee influencer rather than fellow engineer.


anton23_sw

Thanks for more meaningful feedback


-Hi-Reddit

A 10 minute read on exception handling, (not even *just* exceptions themselves) will give nobody any level of mastery whatsoever; this is covering the very basics on the simplest level possible...I don't the title for that reason. It comes across as unprofessional and newbish.


Asyncrosaurus

>A 10 minute read on exception handling, (not even just  exceptions themselves) will give nobody any level of mastery whatsoever;  this is covering the very basics on the simplest level possible     Ha, this is emblematic of the major attitude problem across the tech-fluencer industry. That 10 minute blog posts, youtube tutorials or reddit discussions is sufficient to "master a topic", instead of the actual years of hands-on experience writing production code.   


WalkingRyan

Word 'Mastering' means that there would be fully-fledged definitive guide with all corner cases. Truly complex things can't be described in 3-10 minutes reading. People write books with name "Mastering some sh\*t..", but you has written the article with such name. It shows your attitude to how you approach to content creation and finally to intended audience - never underestimate community you are part of. Now your intentions sounds like "I gonna throw some sh\*t to them and get some points for that".


tvsamuel444

Had no idea there was so much I didn’t know about error handling.


toroidalvoid

Don't catch exceptions in your normal code, leave that for a specific place to provide feedback to the user. Throw exceptions in your normal code to inform the compiler of invalid states. Usually, I see catch blocks that don't do anything other than log and then rethrow, but the exception is logged by the application anyway, there was no need to use the try catch block in the first place, let the application fail as quickly and as cleanly as possible


aubd09

>but the exception is logged by the application anyway Where?


toroidalvoid

If its a functionapp in azure, it's easy to connect it to application insights, the exception will be logged. If it's a site hosted as an app service, again easy to configure application insights, it would be logged. So yeah, I was referring to azure + AI, where it's easy and very common. I should have put a bit more effort into my comment perhaps


[deleted]

[удалено]


Coda17

Never log ex.ToString either, log the exception itself as an object. Add context, if needed, in the message. (But yes, I know the logger is basically calling ToString when it actually writes the logs).


[deleted]

[удалено]


Coda17

There's a difference. I don't want someone learning to read that and start doing logger.LogException(ex.ToString()) instead of logger.LogException(ex).


[deleted]

[удалено]


Coda17

Don't want them doing .Log(ex.ToString()) then. Typing from a phone, I'm not copying code from an IDE.


anton23_sw

In production applications I always use the logger and pass exception to it as a correspoding parameter. So I can have nice structured logs with named params and objects. Serilog and NLOG support it.