一個適合於.NET Core的超輕量級工作流引擎:Workflow-Core

一個適合於.NET Core的超輕量級工作流引擎:Workflow-Core

聽說有workflow的東西,沒有搞明白到底能夠用它做什麼。

最後更新 2023/2/6 下午10:36
Jimmy.Tang
預計閱讀 6 分鐘
分類
.NET
標籤
.NET C#

站長工作中會使用到該技術,所以轉載該篇文章,大家一起了解 workflow,能夠用它做什麼。

文章正文如下:


最近想做一個 OA 相關的網站開發,一直都聽說有 workflow 的東西,之前也斷斷續續學習過 Workflow Foundation 4.0,還是沒有搞明白到底能夠用它做什麼。

但還是覺得 workflow 在某種情形下應該可以適用,雖然還沒有答案,網上搜羅了一通,發現一個 workflow-core 的東西,覺得挺有意思,遂停下來,琢磨一下,現分享與大家。

簡介

workflow core 的 GitHub 主頁:https://github.com/danielgerlag/workflow-core

如主頁上介紹的,workflow core 作為一個輕量級 workflow 引擎,可以嵌入到專案中,其底層是用 .net standard 2.0 開發,可以用來追蹤長時間執行的任務狀態,功能也比較強大,支援外掛形式持久化,和多節點並行處理,貌似很牛。並且目前有給一個 Conductor 專案,就是使用 workflow core 作為核心的 workflow 伺服器(原來執行 workflow,需要單獨的一個伺服器啊),Conductor 這裡就不展開了。workflow core 支援 fluent 語法,寫起來也非常美觀,雖然沒有 WF 那樣有圖形化的操作介面,但感覺程式碼比較乾淨。

  • 插播 .Net Standard 2.0 簡介

開始的時候不瞭解什麼是 .Net Standard 2.0,這篇文章講得比較清楚,.Net Standard 與 .NET Framework 關係,還有這個 .NET Core 2.0 是您的最好選擇嗎,原來微軟為了統一 .Net 的各種平台,出了一個 .Net Standard 標準庫,基於這個庫開發的,可以應用於 .net framework 4.6.1 以上版本,也可以應用於 .net core 2.0 以上。

瞭解了相關內容後,直接打開說明,照著例子走一遭了。

範例 1

新建一個專案,指明使用 .net framework 4.6.1 以上,新建專案後,在 Package Manager Console 中,安裝 workflow core:Install-Package WorkflowCore,安裝這個套件會預設安裝一系列的相依套件。

可能由於版本的關係,還需要另外安裝兩個套件:Microsoft.Extensions.LoggingMicrosoft.Extensions.Logging.Debug。這樣就可以按照 Sample 01 開始編寫程式碼了。

Sample01 是一個 helloworld,包含了幾部分內容:

  1. 構建 StepBody,就是 workflow 中需要執行的內容,每個類別繼承自 StepBody 這個虛擬類別,多載 ExecutionResult Run(IStepExecutionContext context),這個函式中完成所需的工作
public class HelloWorld : StepBody
{
    private ILogger logger;

    public HelloWorld(ILoggerFactory loggerFactory)
    {
        logger = loggerFactory.CreateLogger<HelloWorld>();
    }

    public override ExecutionResult Run(IStepExecutionContext context)
    {
        Console.WriteLine("Hello world, workflow");
        logger.LogInformation("Helloworld workflow");

        return ExecutionResult.Next();
    }
}


public class GoodbyeWorld : StepBody
{
    private ILogger logger;

    public GoodbyeWorld(ILoggerFactory loggerFactory)
    {
        logger = loggerFactory.CreateLogger<GoodbyeWorld>();
    }

    public override ExecutionResult Run(IStepExecutionContext context)
    {
        Console.WriteLine("Workflow, Goodbye");
        logger.LogInformation("Goodbye workflow");

        return ExecutionResult.Next();
    }
}

public class SleepStep : StepBody
{
    private ILogger logger;

    public SleepStep(ILoggerFactory loggerFactory)
    {
        logger = loggerFactory.CreateLogger("SleepStep");
    }

    public override ExecutionResult Run(IStepExecutionContext context)
    {
        Thread.Sleep(1000);

        logger.LogInformation("Sleeped");

        return ExecutionResult.Next();
    }
}
  1. 構建 workflow,實作 IWorkflow 介面,每個 workflow 有一個 Id 和一個 Version,標明這個 workflow 的身份,這裡透過兩種方法構建了 HelloWorkflow,
public class HelloWorkflow : IWorkflow
{
    public string Id => "HelloWorkflow";

    public int Version => 1;

    public void Build(IWorkflowBuilder<object> builder)
    {
        builder.StartWith(context =>
            {
                Console.WriteLine("Hello world");
                return ExecutionResult.Next();
            })
            .Then(context =>
            {
                Thread.Sleep(500);
                Console.WriteLine("sleeped");
                return ExecutionResult.Next();
            })
            .Then(context =>
            {
                Console.WriteLine("Goodbye world");
                return ExecutionResult.Next();
            });
    }
}

public class HelloWorkflow2 : IWorkflow
{
    public string Id => "HelloWorkflow";

    public int Version => 2;

    public void Build(IWorkflowBuilder<object> builder)
    {
        builder.StartWith<HelloWorld>()
            .Then<SleepStep>()
            .Then<GoodbyeWorld>();
    }
}
  1. 萬事俱備,準備讓 workflow 執行起來。第一步當然是搭建 service,Workflow Core 透過 Injection 命名空間的 ServiceCollection 添加了 Workflow 相關的服務,對於有參數的 StepBody,需要先透過 service 的 AddTransient 函式註冊,這樣才能正確的建構物件:
/// <summary>
/// 配置workflow
/// </summary>
/// <returns></returns>
private IServiceProvider ConfigureServices()
{
    //setup dependency injection
    IServiceCollection services = new ServiceCollection();
    services.AddLogging();
    services.AddWorkflow();
    //services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow"));

    // 這些建構函式帶參數的,需要添加到transient中
    services.AddTransient<HelloWorld>();
    services.AddTransient<GoodbyeWorld>();
    services.AddTransient<SleepStep>();

    var serviceProvider = services.BuildServiceProvider();

    //config logging
    var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
    loggerFactory?.AddProvider(new DebugLoggerProvider());

    return serviceProvider;
}

接下來,啟動 workflow 主機,並啟動一次 workflow,將整個表單程式碼貼一下,這樣比較清晰

using Microsoft.Extensions.DependencyInjection;
using System.Windows;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Debug;
using WorkflowCore.Interface;

namespace WorkflowTest;

public partial class MainWindow : Window
{
    IServiceProvider? _serviceProvider = null;
    bool _serviceStarted = false;

    public MainWindow()
    {
        InitializeComponent();
    }

    private void StartWorkflow()
    {
        if (_serviceProvider == null)
        {
            _serviceProvider = ConfigureServices();
            var host1 = _serviceProvider.GetService<IWorkflowHost>();

            host1?.RegisterWorkflow<HelloWorkflow>();
            host1?.RegisterWorkflow<HelloWorkflow2>();
        }


        var host = _serviceProvider.GetService<IWorkflowHost>();
        var wd = host.Registry.GetDefinition("HelloWorkflow");

        // 如果host啟動了,不能再次啟動,但沒有判斷方法
        if (!_serviceStarted)
        {
            host.Start();
            _serviceStarted = true;
        }


        // 啟動workflow工作流
        host.StartWorkflow("HelloWorkflow", 1, data: null); //
        //host.StartWorkflow("HelloWorkflow");//, 2, data: null, 默認會啟用版本高的
    }

    private void StopWorkflow()
    {
        var host = _serviceProvider.GetService<IWorkflowHost>();

        host?.Stop();
        _serviceStarted = false;
    }

    /// <summary>
    /// 配置workflow
    /// </summary>
    /// <returns></returns>
    private IServiceProvider ConfigureServices()
    {
        //setup dependency injection
        IServiceCollection services = new ServiceCollection();
        services.AddLogging();
        services.AddWorkflow();
        //services.AddWorkflow(x => x.UseMongoDB(@"mongodb://localhost:27017", "workflow"));

        // 這些建構函式帶參數的,需要添加到transient中
        services.AddTransient<HelloWorld>();
        services.AddTransient<GoodbyeWorld>();
        services.AddTransient<SleepStep>();

        var serviceProvider = services.BuildServiceProvider();

        //config logging
        var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
        loggerFactory?.AddProvider(new DebugLoggerProvider());

        return serviceProvider;
    }

    private void startButton_Click(object sender, RoutedEventArgs e)
    {
        StartWorkflow();
    }

    private void stopButton_Click(object sender, RoutedEventArgs e)
    {
        StopWorkflow();
    }
}

就這樣一個簡單的 Workflow Core 例子就完成了,總體來說,還是很簡單,清晰的。

本文來自轉載。

作者:Jimmy.Tang

原文標題:一個適合於.NET Core 的超輕量級工作流引擎:Workflow-Core

原文連結:https://www.cnblogs.com/keep-study-to-die/p/12001408.html

站長註:文中程式碼其實引用了不少套件,但原文未說全,站長測試時已將套件補全,測試程式碼見:https://github.com/dotnet9/TerminalMACS.ManagerForWPF/tree/master/src/Demo/WorkflowTest

繼續探索

延伸閱讀

更多文章
同分類 / 同標籤 2026/2/7

AOT使用經驗總結

從專案建立伊始,就應養成良好的習慣,即只要添加了新功能或使用了較新的語法,就及時進行 AOT 發布測試。

繼續閱讀