CustomizableTryParadigm.Release
0.0.9
See the version list below for details.
dotnet add package CustomizableTryParadigm.Release --version 0.0.9
NuGet\Install-Package CustomizableTryParadigm.Release -Version 0.0.9
<PackageReference Include="CustomizableTryParadigm.Release" Version="0.0.9" />
paket add CustomizableTryParadigm.Release --version 0.0.9
#r "nuget: CustomizableTryParadigm.Release, 0.0.9"
// Install CustomizableTryParadigm.Release as a Cake Addin #addin nuget:?package=CustomizableTryParadigm.Release&version=0.0.9 // Install CustomizableTryParadigm.Release as a Cake Tool #tool nuget:?package=CustomizableTryParadigm.Release&version=0.0.9
Customizable Try-Paradigm implementation
Motivation
When developping my DTO Infrastructure framework, I ended up adopting the, as I call it, Try-Paradigm for my methods. This "paradigm" purpose is to transform any methom into its "Try" version, where, instead of throwing an exception, return it with a corresponding boolean indication the failure or success of this "Try" method, and also with its returned value if applicable.
I used a Tuple(bool, T?, Exception?)
in order to represent such data to be returned by those "Try" method. This Tuple
could then be processed to check upton the boolean value if we get the returning value or if we need to handle the exception.
Eventually I ended up refactoring my code to add delegates to allow customization of how to handle this exception, then I realize I should make it as a stand alone library instead, so here is the result of it.
Description
This library revolve around 3 main parts :
Formating any returning value or
Exception
to be thrown in case of "Try" method failure into aTuple(T_Code?, T_Out?, Exception?)
Handling such tuple to either simply return the
T_Out
value in case of success (dertermined byT_Code
value) or perform a custom exception handling.Exposing 4
Action<Exception?>
delegates to allow customization of such handling mechanism.
Returning tuples for "Try" method
This library offer user to call the CustomCatchUtility.ReturningTryTuple(this Exception?, ...)
extension method and their overloaded variant to transmute any Exception
along with the potential T_Out
returning value of the "Try" method into a corresponding Tuple(bool, T_Out?, Exception?)
.
In this implementation though, I ended up being a little more general purpose and abstracted away the returning boolean as a generic T_Code
Type is ever you want to check such "Try" method success or failure along another Type than a boolean (I had in mind ternary value Type). The returned tuple is then a Tuple(T_Code?, T_Out?, Exception?)
.
The overloaded variants are methods that either use bool
as T_Code
and/or no T_Out
value are returned in a first place, so returning a Tuple(T_Code?, Exception?)
instead.
The generic T_Code
variant require that you provide a Func<Exception?, T_Out?, bool, T_Code?>
delegate that will compute the correct T_Code
code status to return given the Exception
and T_Out
arguments of ExceptionToTuple()
and boolean telling if a NULL T_Out
value is a correct returning value for the "Try" method or not.
This method and their overloaded variant are
(bool, Exception?) ReturningTryTuple(this Exception? exception)
(T_Code?, Exception?) ReturningTryTuple<T_Code>(this Exception? exception, Func<Exception?, NoOutValue?, bool, T_Code?> returnCodeDelegate)
(bool, T_Out?, Exception?) ReturningTryTuple<T_Out>(this Exception? exception, T_Out? outValue = default, bool outCanBeNull = false)
(T_Code?, T_Out?, Exception?) ReturningTryTuple<T_Code, T_Out>(this Exception? exception, Func<Exception?, T_Out?, bool, T_Code?> returnCodeDelegate, T_Out? outValue = default, bool outCanBeNull = false)
where NoOutValue
is just an empty struct to mimic a non returning "Try" method (void or Task method).
In order to offer a filter on exceptions that can be handled or not by this library, it expose a collection of Exception
Type
, wich are all the types, and automaticcaly all of their corresponding sub-types, that are handled by this library.
This collection is the exposed HandledExceptions
property, wich is an CustomAddHashSet<Type>
that allow adding only Type
that are sub-types of the top-most Exception
type, that contains by default the defined abstract CustomCatchableException
class. So if you want your custom exceptions to be handled by this library by default, you'll have to extend this CustomCatchableException
class instead of directly extend Exception
.
If this behavior doen't match your requirement, and/or you want to handle other exceptions you don't own, you can add them to HandledExceptions
, or more concisely their common exception type ancestor. That is to say to adding Exception
directly will encapsulate all exception then. You can call the HandleAllExceptions()
method to do so.
Handling "Try" method and their returned tuples
This library offer T_Out? HandleTryMethod<T_Code, T_Out>(this Func<(T_Code?, T_Out?, Exception?)> tryMethod, Func<T_Code?, bool> checkCodeDelegate, Exception? exception = default)
as a method to transfom any "Try" methods to their standard versions that retun direcly only its return value (or void if no value where returned in a first place).
You will need to provide the so said "Try" method along with a Func<T_Code?, bool>
delegate that will interpret the returning T_Code
code status into a boolean indication success or failure of the "Try" method. Not necessary, but you can provide a "root" exception that will be customly handled here. If none are provided, a standard Exception
will be created then. Either way, this resulting exception will encapsulate the "Try" method returning exception as its InnerException
before being handled.
Customize exceptions handling
In case of failure of the corresponding "Try" method, the returning Tuple(T_Code?, T_Out?, Exception?)
will be handled along 4 customizable exposed Action<Exception?>
delegates.
CustomBeforeExceptionHandler
that will actually be called inside the previously describedExceptionToTuple()
method on the "Try" method returning exception in case of failure of this method.CustomExceptionHandler
that will be called on the so called "root" exception (with the "Try" method exception as itsInnerException
). This is where the actual custom exception handling will take place.CustomAfterExceptionHandler
if you ever need a post exception handling process to happen on the "root" exception.CustomNotHandledExceptionHandler
define how exceptions that are not handled by this library (those whose types aren't in theHandledExceptions
collection) should be ... handled (kinda ironic).
Usage
For any "Try" method you are writing, when you decide that the method fail, instead of throwing an Exception
, call one of the overload of exception.ReturningTryTuple(...)
on it , or in case of success directly CustomCatchUtility.ReturningTryTuple(default, yourReturningingvalue, canBeNull)
, depending on what tuple you want to return (see corresponding previous section). This way all your "Try" method will return a tuple ready to be handled by the next main method of this library. If you choose to use another code status than a boolean, then you'll need to provide a delegate to compute such custom code status from the same parameter than those of ReturningTryTuple(...)
.
To handle your "Try" methods, you simple have to create a new methods for each of them that will call CustomCatchUtility.HandleTryMethod(...)
on them along with the forwarded "Try" method arguments and an optional exception to be customly handled in case of failure of the "Try" method. If you choose to use another code status than a boolean, you'll need to provide a delegate that will interprete such custom code status into a boolean value.
To customize how the library will perform its handling, you simply set with your own exception handling logic any or all of the 4 exception delegates decribed in the previous section.
You can call CustomCatchUtility.HandleAllExceptions()
before to handle all kind of exceptions. If not, only those derivating from CustomCatchableException
class will be handled this way.
Finally there are asynchronous variants for each of the defined HandleTryMethod(...)
called HandleTryMethodAsync(...)
.
Exemple
Let say you want a method, int GetCapacity<T>(IEnumerable<T>)
that will return the capacity (Count) of the IEnumerable<T>
argument.
Instead of coding it directly, you can define the logic in another method, preferably called TryGetCapacity<T>(IEnumerable<T>)
that will not return an int
but a (bool, int, Exception?)
tuple instead. To do so, whenever an Exception
occur, lets name it ex
, then return ex.ReturningTryTuple<int>();
; or when you return a computed int
capacity
, instead return CustomCatchUtility.ReturningTryTuple(default, capacity)
.
public (bool, int, Exception?) TryGetCapacity<T>(IEnumerable<T> collection)
{
if (collection == null)
return new ArgumentNullException(nameof(collection))
.ReturningTryTuple<int>();
return CustomCatchUtility.ReturningTryTuple(default, collection.Count());
}
Then you can now define you original int GetCapacity<T>(IEnumerable<T>)
method wich will simply call the HandleTryMethod(...)
method on TryGetCapacity
:
public int GetCapacity<T>(IEnumerable<T> collection)
=> CustomCatchUtility.HandleTryMethod
(
TryGetCapacity,
collection,
new CapacityException($"Call of {nameof(TryGetCapacity)}<{typeof(T).FullName}>({nameof(collection)}) failed!")
);
You can further check the provided IntegrationTesting
project along this library to see simple examples using the library.
Note
This library is available as a nuget package.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Added some Trace.WriteLine() when calling our handler and when checking if an exception is handled or not.