Simple and powerful Railway Oriented library for C#.
using FunctionalUtility.Extensions;
TryExtensions.Try(() => {
Console.Write("Enter a number: ");
var input = Console.ReadLine();
return Convert.ToInt32(input);
})
.OnSuccess(number => Console.WriteLine($"Number is valid: {number}"))
.OnFail(result => {
Console.WriteLine("Result:");
Console.WriteLine($"Is Success? {result.IsSuccess}"); //Is Success? False
Console.WriteLine($"Error title: {result.Detail.Title}"); //Sample: Error title: ExceptionError
Console.WriteLine($"Error message: {result.Detail.Message}"); //Sample: Error message: Input string was not in a correct format.
});
The output of each method is a MethodResult class. The MethodResult class has two important fields.
- IsSuccess: Determines whether the operation was successful or not.
- Detail: It keeps more details of the result. Like title, message, etc.
There are two types of MethodResult:
MethodResult
: Simple MethodResult that have no data.MethodResult<T>
: MethodResult that holds data of type T.
The MethodResult class has two important methods.
-
Ok: Indicates that the operation was successful and can include data and detail.
-
MethodResult.Ok()
-
MethodResult<string>.Ok("Hello")
-
MethodResult.Ok(new ResultDetail(statusCode: 200, "Title", "Message"))
-
MethodResult<string>.Ok("Hello", new ResultDetail(statusCode: 200, "Title", "Message"))
-
-
Fail: Indicates that the operation failed and contains detail.
MethodResult.Fail(new BadRequestError("title", "message"))
using FunctionalUtility.ResultUtility;
// This method returns "Hello" string.
private static MethodResult<string> GetHello() {
return MethodResult<string>.Ok("Hello");
}
public static MethodResult PrintHello() =>
GetHello()
.OnSuccess(message => Console.WriteLine(message))
.OnFail(result => Console.WriteLine($"Operation failed. {result.Detail.Title}-{result.Detail.Message}"));
public static MethodResult PrintHelloVerbose() {
var messageResult = GetHello();
if (!messageResult.IsSuccess) {
//Operation was not successful. Print detail.
var detail = messageResult.Detail;
Console.WriteLine($"Operation failed. {detail.Title}-{detail.Message}");
return MethodResult.Fail(detail);
}
//Operation was successful.
Console.WriteLine(messageResult.Value); //Hello
return MethodResult.Ok();
}
For simplicity, you can customize the ResultDetail:
public class CustomDetail : ResultDetail {
public CustomDetail(int statusCode = 1000, string title = "Custom Title",
string? message = "Custom Message") : base(statusCode, title, message) { }
}
static void PrintCustomDetail() =>
MethodResult.Fail(new CustomDetail())
.OnFail(result => {
var detail = result.Detail;
Console.WriteLine(detail.StatusCode); //1000
Console.WriteLine(detail.Title); //Custom Title
Console.WriteLine(detail.Message); //Custom Message
});
You can also customize the fields:
public class CustomDetail : ResultDetail {
public string CustomField { get; set; }
public CustomDetail(string customField, int statusCode = 1000, string title = "Custom Title",
string? message = "Custom Message") : base(statusCode, title, message) {
CustomField = customField;
}
}
static void PrintCustomDetail() =>
MethodResult.Fail(new CustomDetail("Custom Field"))
.OnFail(result => {
if (result.Detail is CustomDetail customDetail) {
Console.WriteLine(customDetail.CustomField); //Custom Field
}
var detail = result.Detail;
Console.WriteLine(detail.StatusCode); //1000
Console.WriteLine(detail.Title); //Custom Title
Console.WriteLine(detail.Message); //Custom Message
});
It is better to inherit from the ErrorDetail class to customize the error details. Because the ErrorDetail class includes fields like StackTrace and exception data.
ResultDetail is a base class for successful and unsuccessful operations. But the ErrorDetail class is designed for failed operations.
Usually, to increase the security of the program, the details of the errors (such as 500 errors in the server) are not displayed to users. With the ShowDefaultMessageToUser field in the ErrorDetail class you can specify whether the default message is displayed or not.
For example:
static MethodResult Print(bool showDefaultMessage) =>
MethodResult.Fail(new InternalError(message: "This is an important bug",
showDefaultMessageToUser: showDefaultMessage))
.OnFail(result => Console.WriteLine(result.Detail.GetViewModel()));
Print(false);
/*
* Output:
* { StatusCode = 500, Title = InternalError, Message = This is an important bug }
*/
Print(true);
/*
* Output:
* { StatusCode = 500, Title = Error!, Message = something went wrong. }
*/
You can easily add the objects you need to the details.
static MethodResult Print(string input) =>
MethodResult.Fail(new BadRequestError())
.OnFail(result => result.Detail.AddDetail(new {input}))
.OnFail(result => {
if (result.Detail.MoreDetails is null)
return;
foreach (var detail in result.Detail.MoreDetails) {
Console.WriteLine(detail);
}
});
/*
* Sample Output:
* { input = Test }
*/
Types of Result Details that exist:
- BadRequestError.cs
- ConflictError.cs
- ErrorDetail.cs
- ExceptionData.cs
- ExceptionError.cs
- ForbiddenError.cs
- InternalError.cs
- NotFoundError.cs
- NotImplementedError.cs
- UnauthorizedError.cs