The Problem
Exceptions are slow, cumbersome, and often result in unexpected behavior. Even the official Microsoft docs tell you to limit...
For further actions, you may consider blocking this person and/or reporting abuse
Everytime I see the suggestion of using Resultobject instead Exception. I postulate that they don't know the difference between the normal flow of a program and the abnormal (error, exception, ...) flow.
Here in your exemple it's perfectly normal that when searching for something, you don't have a hit. So ofc you should never throw a NotFoundException here. But having a ResultObject here is wrong because when you search something and don't find it, you just return null. But if, while searching your object, you connection to the database vanish, then it makes perfectly sense to throw an exception.
Then you can argue when should I use that kind of pattern ? This pattern makes a lot of sense in command/event programing where, for a command, you have 2 kind results: CommandSuccceed and CommandFailed. Here you keep the exception for "transport" problem and put the failure object for business information. You can see that kind of usage in Microsoft.CognitiveServices for example
I understand the fundamental difference between normal flow and the abnormal.
This example is admittedly a bit unrealistic: If the object is not found, you can just return null.
Like I mentioned in the article, you can return an Exception object in the result instead of throwing it if you want custom Exception types. After using rust, I found that using a Result with a match function forces you to handle both success and failure and leaves you many options in handling that error. Result is malleable and you can sculpt it to fit your system's needs.
You seem like a seasoned developer based on your profile, I'm sure you've come across codebases with tons of try-catch blocks for normal flows that could be better handled with something as simple as the solution I offered. And even for abnormal flows, you can have result types with custom types to differentiate those if you willed. Or if it's fatal, just let the exception be thrown and catch it in the middleware in those special cases.
I personally never fiddled with CQRS but what you said about it being ideal with commands and events makes sense.
I dont understand your argument. An empty search result would totally fit this pattern:
your failed database connection also fits in:
Would mainly use it in error prone tasks such as io, sockets and so on.
last year I had an opportunity to try this approach to error handling on one of short-term projects
In theory, the author is right: less resource-consumable and much faster with result types approach. In practice, the approach is applicable only to pretty "thin" layers, e.g. DAL or BL (moreover, since different layers have different models, you'll have to convert (or at least explicitly pass-through) these error types (models) on layer boundaries). Otherwise, you'll have to write tedious "if" statements and convert results from one type to another, which is difficult to maintain.
So for myself I have decided to go with the result-based approach only on small projects (with "thin" layers or even one-layer app), otherwise, traditional exception-based approach is a way to go.
Additionally, lots of MS or 3P libraries throw exceptions, so handling exceptions is almost unavoidable. Sure, it's not the same as throwing, but anyway, a call stack in such cases may be more useful, then just exception message.
Yeah, I agree. If it's within the normal flow of the application, a result type is expedient.
For example, I'm working with a quiz-taking platform; when the user wants to start the quiz, it may be deleted just then so I return an error "Quiz was deleted", it's due date may have passed after he opened the page client side : "Due date has passed", etc.
In this case, I just convert the result from the service layer to an http response at the controller. If it's an error I return a bad request with the message, otherwise I return the result.
I like this solution. Can I use it for methods which don't return a value (void)?
Maybe something like F# Unit?
Or similar to MediatR Unit.
Hello, Alexander.
I use Unit. I have a global variable imported as unit so I can just do something like this:
I tried it with MediatR Unit and it seems to work nicely.
I would argue the opposite. The standard error handling in .NET is using exceptions. Returning types as errors is so wrong in so many ways. The biggest issue is, it swallows actual error messages from telemetry.
You can argue that you can try catch the error, log it and then return the result type. What's the point of having result types then?
I never argued to try catch an error then return a result type. Languages with no exception throwing like go and rust handle this by simply using custom error types. You could use different classes to represent different error types in your result or just return an exception in your result instead of throwing it like I mentioned in the article. This is not a silver bullet but I personally find it much more logical than try catch finally blocks.
If a fatal exception happens, just let it propogate and be caught by some middleware but that should only ever happen for errors you can't really handle such as the DB connection being closed
What I prefer is simply let the caller take care of data integrity. If methods dont receive valid data, just throw an exception. That way you dont lose stack trace. Caller should handle the exceptions it wants and let other exceptions go through.
Finally, you can have a middleware to catch the remaining errors.
Totally agree, e.g. @forreason 's code above
implies catching an exception, and putting its message into
IResult.Message
propertyso no point in "struggling" with exceptions replacing them with
IResult
when you have try/catch'esit's better to admit and accept exceptions :)
also agree with @pitming, people often don't distinguish between normal and abnormal flows trying to blame those who uses exceptions in implementing business logic with exceptions.