本以為跟國際化無緣,不在軟體的考慮範圍內,沒想到偶爾一個項目還是繞不開,小記一下System.Globalization.CultureInfo.InstalledUICulture.Name 這是獲得英文的區域碼,也就是zh-cn的這種,需要找區域碼字典對照。 System.Globalizati ...
本文將和大家推薦我團隊開源的 LightWorkFlowManager 輕量的工作過程管理庫,適合任何需要執行工作過程的應用邏輯,可以方便將多個工作過程拼湊起來,且自動集成重試和失敗處理,以及日誌和上報功能
這個 LightWorkFlowManager 輕量的工作過程管理庫是我所在的團隊在 GitHub 上使用最友好的 MIT 協議開源的庫,請看 https://github.com/dotnet-campus/LightWorkFlowManager
這個 LightWorkFlowManager 輕量的工作過程管理庫現在已經在我團隊的正式產品應用上運行有半年了,相對來說比較穩定。使用過程中如果有任何問題,都歡迎在 GitHub 上新建 Issues 進行反饋
世界上有許多現有的工作過程管理,那為什麼還重新造了一個?沒啥原因,自己造的用的順手。這個 LightWorkFlowManager 庫在功能上和其他的工作過程管理庫相比要缺失許多,但勝在輕量。且 LightWorkFlowManager 最重要的賣點就是對調試友好,適合用在本身複雜度就比較好的邏輯情況下,可以有效降低引入工作過程管理模塊本身帶來的額外複雜度
許多現有的功能齊全的工作過程管理庫都是在 ASP.NET Core 上使用的。然而我這裡的需求是在客戶端應用框架上使用,且大部分情況下是在用戶的設備上運行的,許多服務依賴本身是不存在的。儘管許多現有的功能齊全的工作過程管理庫都可以通過配置的形式將其切換到單機內運行,然而這樣做將在大部分時候不是庫的推薦用法,用起來沒有那麼順。為此我所在的團隊開發了 LightWorkFlowManager 輕量的工作過程管理庫,這個庫可以很方便在客戶端框架上運行,比如在 WPF 應用程式、在 MAUI 應用程式上運行。同時 LightWorkFlowManager 也兼顧 ASP.NET Core 服務本身,特別是依賴註入部分,可以和預設的服務的依賴註入相連接
以下是 LightWorkFlowManager 輕量的工作過程管理庫的使用方法
按照 dotnet 的慣例,先安裝 NuGet 庫,請看 https://www.nuget.org/packages/dotnetCampus.LightWorkFlowManager
在開始介紹使用方法之前,需要先介紹一下 LightWorkFlowManager 裡面的概念。在 LightWorkFlowManager 裡面定義了 MessageWorkerManager 類型,將其作為工作器的管理器,用途就是將各個工作器串聯起來,且運行起來,這個 MessageWorkerManager 就是 LightWorkFlowManager 的入口類型
在 LightWorkFlowManager 裡面定義了工作器的基礎類型就是 MessageWorker 類型,所有的業務上的開發者自定義的工作器都需要直接或間接繼承 MessageWorker 類型
瞭解基礎概念之後,接下來就開始編寫一個簡單的邏輯作為例子
使用方法
1、 創建 MessageWorkerManager 對象。創建 MessageWorkerManager 工作器管理器即可作為承載工作器的框架,請為每次單獨的任務創建獨立的 MessageWorkerManager 對象
// 每個任務一個 TaskId 號
string taskId = Guid.NewGuid().ToString();
// 相同類型的任務採用相同的名字,比如這是做 PPT 解析的任務
string taskName = "PPT 解析";
// 提供容器
IServiceScope serviceScope = serviceProvider.CreateScope();
var workerManager = new MessageWorkerManager(taskId, taskName, serviceScope);
2、 定義 Worker 工作器。以下例子代碼定義了模擬業務代碼的 FooWorker 工作器,可以在 FooWorker 重寫的 DoInnerAsync 方法裡面編寫執行的業務代碼。以下代碼的 InputType 和 OutputType 分別是工作器的輸入和輸出類型,每個工作器的輸出對象都會自動加入到 MessageWorkerManager 的過程上下文緩存裡面
record InputType();
record OutputType();
class FooWorker : MessageWorker<InputType, OutputType>
{
protected override ValueTask<WorkerResult<OutputType>> DoInnerAsync(InputType input)
{
...
}
}
3、 執行 Worker 工作器。 完成類型的定義之後,接下來演示如何通過 MessageWorkerManager 將 FooWorker 運行起來
var result = await workerManager
.GetWorker<FooWorker>()
.RunAsync();
以上代碼也可以簡寫為以下代碼
var result = await workerManager.RunWorker<FooWorker>();
以上代碼運行的前提是先註入 FooWorker 所需的 InputType 類型的對象。下文將告訴註入工作器參數部分的更多細節
機制和功能
工作器參數
工作器參數通過 MessageWorkerManager 工作器管理的 IWorkerContext 上下文讀取。每個工作器的可返回值類型都會自動被設置到 IWorkerContext 上下文裡面。如此即可自動實現上一個 Worker 的輸出作為下一個 Worker 的輸入
在每個工作器裡面,都可以通過 SetContext
設置上下文信息
在開始執行工作器時,還可以手動設置輸入參數,如以下例子
// 例子1:先獲取工作器,再賦值給到工作器的執行方法
await Manager
.GetWorker<FooWorker>()
.RunAsync(new InputType());
// 例子2: 通過 SetContext 進行設置參數,再執行工作器
await Manager
.SetContext(new InputType())
.RunWorker<FooWorker>();
如果有些工作器之間的輸入和輸出參數需要進行轉換,也可以 SetContext
傳入轉換委托進行參數轉換
// 以下例子將當前上下文里的 Foo1Type 類型轉換為 FooWorker 需要的 Foo2Type 參數
await Manager
.SetContext((Foo1Type foo1) => ConvertFoo1ToFoo2(foo1))
.RunWorker<FooWorker>();
異常中斷和重試
每個 Worker 都可以返回 WorkerResult 類型的返回值,可以在返回值裡面告知框架層是否當前的 Worker 執行成功。在執行失敗時,可以賦值錯誤碼,方便定位調試或輸出。在執行失敗時,可以返回告知框架層是否需要重試
中斷後續的工作器執行有兩個方法:
方法1: 通過返回狀態為失敗的 WorkerResult 返回值。一旦工作管理器的狀態為 IsFail 狀態,將會阻止所有的沒有標記 CanRunWhenFail 為 true 的工作器的執行。換句話說就是除了哪些不斷成功或失敗狀態都要執行的 Worker 工作器之外,其他的工作器都將不會執行,包括 SetContext 裡面的委托轉換也不會執行
方法2: 通過拋出異常的方式,通過 dotnet 裡面的異常可以讓後續邏輯炸掉不再執行
以上兩個方法都是框架推薦使用的。框架設計的偏好是如果在重視性能的情況下,儘量使用方法1的方式中斷執行。如果是在複雜的業務邏輯,有大量業務邏輯穿插在工作過程之外,則可以方便通過方法2進行中斷
在 Worker 里執行其他 Worker 工作器
在一個 Worker 裡面,可以執行其他的 Worker 工作器,如此可以比較自由的實現分支邏輯,套娃決定執行工作器
例子如下:
假定有一個 Worker2 工作器,定義如下:
class Worker2 : MessageWorker<InputType, OutputType>
{
protected override ValueTask<WorkerResult<OutputType>> DoInnerAsync(InputType input)
{
return SuccessTask(new OutputType());
}
}
record InputType();
record OutputType();
有另一個 Worker1 工作器,可以在 Worker1 裡面執行 Worker2 工作器:
class Worker1 : MessageWorkerBase
{
public override async ValueTask<WorkerResult> Do(IWorkerContext context)
{
await Manager
.GetWorker<Worker2>()
.RunAsync(new InputType());
return WorkerResult.Success();
}
}
委托工作器
有一些非常小且輕的邏輯,也想加入到工作過程裡面,但不想為此定義一個單獨的工作器。可以試試委托工作器,如以下代碼例子
var delegateMessageWorker = new DelegateMessageWorker(_ =>
{
// 在這裡編寫委托工作器的業務內容
});
var result = await messageWorkerManager.RunWorker(delegateMessageWorker);
如果連 DelegateMessageWorker 都不想創建,那也可以直接使用 MessageWorkerManager 的 RunWorker 方法傳入委托,如以下代碼例子
await messageWorkerManager.RunWorker((IWorkerContext context) =>
{
// 在這裡編寫委托工作器的業務內容
});
博客園博客只做備份,博客發佈就不再更新,如果想看最新博客,請到 https://blog.lindexi.com/
本作品採用知識共用署名-非商業性使用-相同方式共用 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發佈,但務必保留文章署名[林德熙](https://www.cnblogs.com/lindexi)(包含鏈接:https://www.cnblogs.com/lindexi ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請與我[聯繫](mailto:[email protected])。