.NET 結果與錯誤處理利器 FluentResults

小码编匠發表於2024-08-01

前言

在專案開發中,方法返回的結果(成功或失敗)對我們開發來說很重要。傳統方法,如透過異常來指示錯誤或使用特定的返回型別(如布林值加輸出引數),雖然有效,但可能缺乏直觀性和靈活性。

FluentResults庫應運而生,它以一種既流暢又富有表達力的方式,極大地最佳化了這一過程。透過使用FluentResults,能夠以一種更加自然和易於理解的方式傳遞操作結果,包括成功狀態、錯誤資訊、警告以及額外資訊,提高程式碼的可讀性和可維護性。

這種方式不僅讓錯誤處理更加集中和一致,還使得程式碼結構更加清晰,邏輯更加流暢。

專案介紹

FluentResults 是一個在 .NET 環境中廣泛使用的庫,它提供了一種優雅的方式來處理方法執行的結果和錯誤。

使用 FluentResults,可以很容易地建立包含成功值、錯誤、警告或資訊的物件,並透過鏈式呼叫來處理這些物件。

那麼如何使用 FluentResults 來優雅地處理結果和錯誤資訊呢?

使用 FluentResults

1、安裝 FluentResults

首先,在專案中安裝 FluentResults,可以透過 NuGet 包管理器來安裝。在 Visual Studio 中也可以透過 NuGet 包管理器控制檯輸入以下命令:

Install-Package FluentResults

或者,在專案檔案中新增 NuGet 包引用。

2、建立 Result 物件

使用 Result 類的靜態方法來建立結果物件。Result 類提供了多種方法來建立不同型別的結果,例如成功、失敗、帶有警告或資訊的成功等。

using FluentResults;  
​
static  void Main(string[] args)
{
     var result = IsInteger("");
​
     if (result.IsSuccess)
     {
         Console.WriteLine($"結果:{result.Value} ");
     }
     else
     {
         Console.WriteLine($"結果:{result.Reasons[0].Message}|{result.Errors[0].Message}");
     }
}
​
public static Result<int>  IsInteger(string input)
{
     // 假設輸入為空或null,我們可以選擇認為它不是數字  
     if (string.IsNullOrWhiteSpace(input))
     {
         return Result.Fail<int>("輸入為空或null,無法判斷是否是數字");
     }
     // 使用int.TryParse嘗試將輸入轉換為整數  
     // 如果轉換成功,out引數將包含轉換後的值,方法返回true  
     // 如果轉換失敗,方法返回false  
     if (int.TryParse(input, out int result))
     {
         return Result.Ok(result);
     }
     // 如果無法轉換為整數,則認為輸入不是數字  
     return Result.Fail<int>("輸入不是數字");
}

執行結果

透過使用Result 類我們可以看到,方法執行返回了標準的介面引數,包括IsSuccess,Message,Errors等引數,幫我們快速實現返回結構。

3、鏈式處理結果

FluentResults 允許你透過鏈式呼叫來處理結果,這使得錯誤處理和邏輯流程更加清晰和直觀。

需要注意的是FluentResults 本身的 Result 型別並不直接提供 OnSuccessOnFailure 這樣的鏈式方法,因為這些方法可能是在 FluentResults 的某個版本中以擴充套件方法的形式新增的,或者是在基於 FluentResults 的自定義擴充套件中定義的。

自定義擴充套件類

  /// <summary>
   /// Result 擴充套件方法
   /// </summary>
   public static class ResultExtensions
   {
       /// <summary>
       /// 成功回撥
       /// </summary>
       /// <param name="result"></param>
       /// <param name="successAction"></param>
       /// <returns></returns>
       public static Result OnSuccess(this Result result, Action successAction)
       {
           if (result.IsSuccess)
           {
               successAction?.Invoke();
           }
           return result; // 返回結果以支援鏈式呼叫  
       }
​
       /// <summary>
       /// 失敗回撥
       /// </summary>
       /// <param name="result"></param>
       /// <param name="failureAction"></param>
       /// <returns></returns>
       public static Result OnFailure(this Result result, Action<IError> failureAction)
       {
           if (!result.IsSuccess && result.Errors!= null)
           {
               foreach (var error in result.Errors)
               {
                   failureAction?.Invoke(error);
               }
           }
           return result; // 返回結果以支援鏈式呼叫  
       }
   }

自定義方法

/// <summary>
/// 驗證輸入字串是否為整數
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static Result IsIntegerInfo(string input)
{
    // 假設輸入為空或null,我們可以選擇認為它不是數字  
    if (string.IsNullOrWhiteSpace(input))
    {
        return Result.Fail("輸入為空或null,無法判斷是否是數字");
    }
    // 使用int.TryParse嘗試將輸入轉換為整數  
    // 如果轉換成功,out引數將包含轉換後的值,方法返回true  
    // 如果轉換失敗,方法返回false  
    if (int.TryParse(input, out int result))
    {
        return Result.Ok();
    }
    // 如果無法轉換為整數,則認為輸入不是數字  
    return Result.Fail("輸入不是數字");
}

呼叫示例

 var result = IsIntegerInfo("")
     .OnSuccess(() =>
     {
             // 處理成功的情況  
             Console.WriteLine("Success!");
     })
     .OnFailure(error =>
     {
         // 處理失敗的情況  
         Console.WriteLine("Failed: " + error.Message);
     });
 // 注意:在 OnSuccess 或 OnFailure 中使用 result 變數可能不是安全的,  
 // 因為這些回撥可能在這些回撥執行之前就被修改了。  
 // 更好的做法是在 OnSuccess/OnFailure 的 lambda 表示式中使用區域性變數。

執行結果

在這個示例中定義了兩個擴充套件方法 OnSuccessOnFailure,它們分別接受成功和失敗時要執行的回撥函式。這些方法首先檢查 Result 物件的狀態,然後根據狀態呼叫相應的回撥函式。最後,它們返回原始的 Result 物件,以支援鏈式呼叫。

請注意,示例是為了說明目的而簡化的,並且可能不包含 FluentResults 庫中實際可用的所有功能和最佳化。在實際應用中,應該檢視 FluentResults 的文件和原始碼,以瞭解提供的具體功能。

4、FluentResults 高階特性

FluentResults提供許多高階特性,如鏈式呼叫、自定義錯誤型別、以及包含額外資料和後設資料的錯誤物件。

例如,可以使用Result.Fail的過載版本來包含更多的上下文資訊

return Result.Fail("輸入錯誤.").WithError("The input value must be greater than zero.");

5、自定義 Result 型別

FluentResults 還支援透過繼承 Result 類來建立自定義的結果型別,以便在結果中攜帶額外的資料或狀態。

public class CommonResult
{
    public Result Result { get; }
    public string MyData { get; }
​
    public CommonResult(Result result, string myData)
    {
        Result = result;
        MyData = myData;
        Console.WriteLine($"{nameof(CommonResult)}: {MyData}|{result}");
    }
}

呼叫示例

 public static CommonResult DemoResult(string input)
 {
     bool isSuccess =false;
     string errorMessage = "輸入的字串不是數字";
     string myData = "測試一下";
​
     Result result = isSuccess ? Result.Ok() : Result.Fail(errorMessage);
     return new CommonResult(result, myData);
 }

執行結果

透過以上步驟,可以在 .NET 應用快速、方便的使用 FluentResults 來處理結果和錯誤。可以提高程式碼的可讀性和可維護性,還可以使錯誤處理更加集中和統一規範。

使用場景

  • API 開發:在處理 HTTP 請求和響應時,FluentResults 構建清晰、一致和易於理解的錯誤響應。
  • 業務邏輯驗證:在執行業務邏輯驗證時,FluentResults 可以驗證多個錯誤,並一次性返回。
  • 複雜操作的結果處理:當需要處理包含多個步驟的複雜操作時,FluentResults 可以幫助管理每個步驟的結果,並將它們組合成一個最終的結果。

總結

FluentResults 提供了豐富的 API,可以靈活使用,與現有的 .NET 程式碼庫和框架整合,如 ASP.NET Core、Entity Framework 等,還可以與其他第三方庫一起使用,以提供更全面的錯誤處理和結果功能。

如果你的專案中需要一種更好的方式來處理結果,並希望提高程式碼的可讀性和可維護性,那麼 FluentResults 是一個不錯的選擇。

開源地址

https://github.com/altmann/FluentResults

如果覺得這篇文章對你有用,歡迎加入微信公眾號 [DotNet技術匠] 社群,與其他熱愛技術的同行交流心得,共同成長。

相關文章