dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.4k stars 10k forks source link

Throw and catch exceptions instead of `out string errorMessage` everywhere #2920

Closed HappyNomad closed 6 years ago

HappyNomad commented 6 years ago

While implementing JSON Patch IAdapters, I encountered a coding pattern that I'm unaccustomed to. Instead of throwing and catching exceptions, the library has out string errorMessage parameters everywhere. That's what exceptions are for, so this pattern doesn't seem to belong.

Eilon commented 6 years ago

Exceptions are nice when you want to just "stop everything and give up," but are not so great for "normal control flow of errors," which is probably the intention of the code here. That is, it's perfectly normal for some mismatches to take place when performing a JSON PATCH operation, and using exceptions to flow that has its own set of problems. For example, exceptions are not "contractual" - that is, there's no verifiable way to know that an exception gets thrown, you have to read docs or read code. But having an explicit parameter makes it clear you should expect possible failures, and by what mechanism.

HappyNomad commented 6 years ago

Earlier I also thought there may be some special circumstances here that necessitate the pattern over exceptions. But after having dug into the code, I see that "stop everything and give up," is what the code does anyway. That is, if an invalid condition is met, then processing of the JSON Patch document stops.

Eilon commented 6 years ago

@HappyNomad I think there's still a difference with exceptions in that an exception can sort of "skip over" stack frames until someone does a catch, and then even that catch would catch all exceptions, even a random NullReferenceException or ArgumentException, whereas by flowing errors in a more structured way helps guarantee that the system is dutifully tracking only the types of errors it wishes to track.

HappyNomad commented 6 years ago

@Eilon There's already a JsonPatchException class defined in the Microsoft.AspNetCore.JsonPatch.Exceptions namespace. Once all the out string errorMessages bubble up the call stack to ObjectAdapter, this exception is what's thrown anyway. Replacing a stack of Try calls and their out string errorMessage parameters with a simple catch ( JsonPatchException ) would not catch a "random NullReferenceException or ArgumentException". Rather, it would facilitate "flowing errors in a more structured way" and thus "guarantee that the system is dutifully tracking only the types of errors it wishes to track".

HappyNomad commented 6 years ago

I just happened to come across this code:

https://github.com/aspnet/JsonPatch/blob/8504fa47121430d6704fc0ef309e658c9803f5a9/src/Microsoft.AspNetCore.JsonPatch/JsonPatchDocument.cs#L180-L208

In spite of those numerous nested Try methods and their out string errorMessage parameters, the error is simply converted to a JsonPatchException which is thrown in ObjectAdapter and caught as you see in this code. All those nested Try methods and out string errorMessage parameters are for naught.

Eilon commented 6 years ago

@HappyNomad I think that one way or another it was a design decision (even if not the greatest one), so it would be difficult to change it without making a breaking change. Most of what I said earlier is from memory and general thoughts.