.NET Core向けの超軽量ワークフローエンジン:Workflow-Core

.NET Core向けの超軽量ワークフローエンジン:Workflow-Core

ワークフローに関するものを聞いたことはあるが、それで何ができるのかよくわかっていません。

最終更新 2023/02/06 22:36
Jimmy.Tang
読了目安 5 分
カテゴリ
.NET
タグ
.NET C#

管理者が業務で本技術を使用するため、この記事を転載します。Workflow の概要とその活用方法について皆さんと共有します。

記事本文は以下の通りです。


最近、OA 関連の Web サイト開発をしたいと考えており、以前から 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 はあなたの最良の選択か。Microsoft は .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。このパッケージをインストールすると、デフォルトで一連の依存パッケージもインストールされます。

バージョンの関係か、さらに2つのパッケージをインストールする必要があるかもしれません: 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 の識別子となります。ここでは2つの方法で 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 を実行させてみましょう。まずはサービスを構築する必要があります。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 ホストを起動し、1回 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/04/22

各OSバージョンの.NETサポート状況(250707更新)

仮想マシンとテストマシンを使用して、各OSバージョンの.NETサポート状況を確認します。OSインストール後、対応するランタイムをインストールし、Stardustエージェントを実行できることを確認します(合格条件)。

続きを読む
同じカテゴリ / 同じタグ 2026/02/07

AOTの使用経験のまとめ

プロジェクト作成当初から、新機能を追加したり新しい構文を使用したりした場合には、すぐにAOT公開テストを実施するという良い習慣を身につけるべきです。

続きを読む