ディレクトリ
オープンソースのワークフローエンジンワークフローコア研究とチュートリアル
フロントワークフローオブジェクトと使用
あいまいさを避けるために、事前に合意されました。
ワークフロー多くのノードがあり、ノード点は、ステップ(ステップ)となります。
1、IWorkflow / IWorkflowBuilder
ワークフローコアはワークフロークラスの継承を構築するために使用されるIWorkflow
、ワークフロータスクが開始または実行()メソッド、または作業フロー分岐を得る他の方法を示すことができ、ワークフロールールの代わりに仕事を持っています。
同じ名前が2つのインタフェースを有しているIWorkflow:
public interface IWorkflow<TData>
where TData : new()
{
string Id { get; }
int Version { get; }
void Build(IWorkflowBuilder<TData> builder);
}
public interface IWorkflow : IWorkflow<object>
{
}
ID:このワークフローの一意の識別子。
バージョン:このワークフローのバージョン。
void Build
:この方法でワークフローの構築。
運用プロセスのワークフロー、データを転送することができます。二つの配信方法:入ってくる流れがワークから実行されるときに、ジェネリックを使用して、別工程で製造され、次のノードに渡される単純なオブジェクトタイプを、使用。
論理規則を持つワークフローを構築IWorkflowBuilderワークフローオブジェクト、。循環を有する複合体を構築することができ、ワークフロールールを決定し、または並列または非同期処理ワークフロータスクに。
シンプルなワークフロールール:
public class DeferSampleWorkflow : IWorkflow
{
public string Id => "DeferSampleWorkflow";
public int Version => 1;
public void Build(IWorkflowBuilder<object> builder)
{
builder
.StartWith(context =>
{
// 开始工作流任务
Console.WriteLine("Workflow started");
return ExecutionResult.Next();
})
.Then<SleepStep>()
.Input(step => step.Period, data => TimeSpan.FromSeconds(20))
.Then(context =>
{
Console.WriteLine("workflow complete");
return ExecutionResult.Next();
});
}
}
2、EndWorkflow
このオブジェクトは、タスクが完了している現在のワークフローを表し、ワークフローまたはワークフロータスクのメインブランチの完了を示すことができます。
/// Ends the workflow and marks it as complete
IStepBuilder<TData, TStepBody> EndWorkflow();
ワークフローが独立して動作しているため、各分岐が発生する可能性があり、そのライフサイクル、ワークフローの各支店を持っています。
3,容器
ForEach
、While
、If
、When
、Schedule
、Recur
それは、コンテナのステップ。リターンIContainerStepBuilder<TData, Schedule, TStepBody>
パラレル、佐賀コンテナステップ、リターンIStepBuilder<TData, Sequence>
。
スケジュール、もし、一方のForEach、インタフェースの戻り値の型の次の曜日:
public interface IContainerStepBuilder<TData, TStepBody, TReturnStep>
where TStepBody : IStepBody
where TReturnStep : IStepBody
{
/// The block of steps to execute
IStepBuilder<TData, TReturnStep> Do(Action<IWorkflowBuilder<TData>> builder);
パラレル、佐賀:
/// Execute multiple blocks of steps in parallel
IParallelStepBuilder<TData, Sequence> Parallel();
/// Execute a sequence of steps in a container
IStepBuilder<TData, Sequence> Saga(Action<IWorkflowBuilder<TData>> builder);
言い換えれば、foreachのは、もし、一方で、ときに、スケジュールは、再発は本当のコンテナです。
私の理解によると、それは継承されIContainerStepBuilder
、容器、プロセス/コンテナ内のステップであり、ワークフローコアインタフェースは明らかに表現しているためという名前の著者This a container
。
それはプロセスを含むステップとすることができる操作のセットを含んでいるため、プロセスは、から成る操作の一連である組成物、線状、シーケンシャル。インサイドワークフロー(ワークフロー)があります。
そしてParllel、佐賀は、容器のステップに対応する点。
回路がIContainerStepBuilder継承されることをより直感的な理解シリーズ機器用容器は、シーケンシャルです。
Parllelは容器並列回路/デバイスフローに回路および回路の複数ように、両方のスイッチとしてだけでなく、電気回路を含んでいます。これは、多分岐非同期化と独立したワークフロー、複数のを生成することができます。
実装の観点からインターフェイス、foreachの、一方で、もし、スケジュールは、の曜日、Parllelは達成しているDo()
方法を、そして佐賀は具体化しませんでした。
佐賀について、後述します。
図4に示すように、ワークフロー・ポイントのステップ
次のようにインターフェイスを実装します。
IStepBuilder<TData, TStep> StartWith<TStep>(Action<IStepBuilder<TData, TStep>> stepSetup = null) where TStep : IStepBody;
IStepBuilder<TData, InlineStepBody> StartWith(Func<IStepExecutionContext, ExecutionResult> body);
IStepBuilder<TData, ActionStepBody> StartWith(Action<IStepExecutionContext> body);
IEnumerable<WorkflowStep> GetUpstreamSteps(int id);
IWorkflowBuilder<TData> UseDefaultErrorBehavior(WorkflowErrorHandling behavior, TimeSpan? retryInterval = null);
メソッド名 | 説明 |
---|---|
startsWith | タスクを開始し、このメソッドを呼び出す必要があります |
GetUpstreamSteps | IDのステップ(StepBody)を得ます |
UseDefaultErrorBehavior | 不明 |
StepBodyは、IStepBuilder、ワークフロー、非同期タスクの枝を開始するには、唯一のStartWithを通じて、ノードを構築するノードです。
UseDefaultErrorBehavior
私はナンセンスではない、に使用していませんでした。異常がステップ点で発生した場合、一見トランザクションに関連付けられ、再試行など、終了してもよいです。
二、IStepBuilderノード
IStepBuilderは、平行、非同期、循環などの他の操作を含むことができるノード、またはコンテナを表します。
1、プロパティの設定
名前:このステップポイントの名前を設定し、
ID:ステップポイントの一意の識別子を。
/// Specifies a display name for the step
IStepBuilder<TData, TStepBody> Name(string name);
/// Specifies a custom Id to reference this step
IStepBuilder<TData, TStepBody> Id(string id);
2、設定データ
先に述べたように、2つの方法でワークフロー転送ポイントデータの各ステップ。
TDATA(ジェネリック)は、データの普及により、オブジェクトは、ワークフロー全体で生き残る、ワークフローです。
例えば、ニック・マイデータの場合
class RecurSampleWorkflow : IWorkflow<MyData>
{
public string Id => "recur-sample";
public int Version => 1;
public void Build(IWorkflowBuilder<MyData> builder)
{
...
}
}
public class MyData
{
public int Counter { get; set; }
}
3、入力/出力
現在のポイント(StepBody)にデータを設定するステップは、さらにTDATAデータを提供してもよいです。
データの2つのタイプ:各ステップは、多くの点フィールド、プロパティおよびメソッドを有することができ、ワークフロー転送TDATA。
これらの特定の設定の入力、出力データ。
IStepBuilder<TData, TStepBody> Input<TInput>(Expression<Func<TStepBody, TInput>> stepProperty, Expression<Func<TData, TInput>> value);
IStepBuilder<TData, TStepBody> Input<TInput>(Expression<Func<TStepBody, TInput>> stepProperty, Expression<Func<TData, IStepExecutionContext, TInput>> value);
IStepBuilder<TData, TStepBody> Input(Action<TStepBody, TData> action);
IStepBuilder<TData, TStepBody> Output<TOutput>(Expression<Func<TData, TOutput>> dataProperty, Expression<Func<TStepBody, object>> value);
第三に、ワークフローのノードと論理演算
コンテナの操作
1、佐賀
容器内の一連の動作を実行します。
/// Execute a sequence of steps in a container
IStepBuilder<TData, Sequence> Saga(Action<IWorkflowBuilder<TData>> builder);
「コンテナ内の一連の操作を実行するために使用される、」注記が、実際に、それは本当にありませんが、「コンテナ。」
それが継承していないのでIContainerStepBuilder
、また達成Do()
。
しかし、それは返しSequence
達成しますContainerStepBody
。
佐賀だけではなく、特定の湖よりも、川の名前かもしれませんが、コンテナは、長い川は(対応して保管することができます)実際の湖と等価である場合。
か、そのstatic void Main(string[] args)
コードのあまりにも多くありますが、その新しい方法、それにコードの一部。あなたは正しい、やり方で書かれたすべてのコードを置くことができませんか?次いで、可読性を高めるために、さまざまな方法に、複数の部分にクラス、コードを作成します。自然は変更されません。
佐賀のトランザクション処理は、再試行またはロールバック操作のためのように、使用することができます。後述します。
通常のノード
その後1、
共通ノードを作成し、次のノードを作成するために使用します。これは、メインワークフロー(最外層)のノード、又はループとして、ノードの条件ノード、ノード内のノードであってもよいです。
IStepBuilder<TData, TStep> Then<TStep>(Action<IStepBuilder<TData, TStep>> stepSetup = null) where TStep : IStepBody;
IStepBuilder<TData, TStep> Then<TStep>(IStepBuilder<TData, TStep> newStep) where TStep : IStepBody;
IStepBuilder<TData, InlineStepBody> Then(Func<IStepExecutionContext, ExecutionResult> body);
IStepBuilder<TData, ActionStepBody> Then(Action<IStepExecutionContext> body);
2、アタッチ
その後、通常ノードとして、順次行います。操作オブジェクトの種類は、StepBodyあります。
またStepBodyを実行するIDで指定された共通ノード、特別な意味を取り付けます。これは、ジャンプ制御プロセスとして使用することができます。
goto文の同等。
/// Specify the next step in the workflow by Id
IStepBuilder<TData, TStepBody> Attach(string id);
イベント
1、WAITFOR
イベント、イベントノードとして現在のノードを定義し、次のノードに、バックグラウンドで、ワークフローをハングアップするために使用します。作業の流れが停止する前に、次のように指定することができ識別子(ID)トリガイベントを。ワークフローでは、各イベントの識別子が一意です。
IStepBuilder<TData, WaitFor> WaitFor(string eventName, Expression<Func<TData, string>> eventKey, Expression<Func<TData, DateTime>> effectiveDate = null, Expression<Func<TData, bool>> cancelCondition = null);
IStepBuilder<TData, WaitFor> WaitFor(string eventName, Expression<Func<TData, IStepExecutionContext, string>> eventKey, Expression<Func<TData, DateTime>> effectiveDate = null, Expression<Func<TData, bool>> cancelCondition = null);
条件付きボディとループ
1、終了
それはノード実行の最後でなければなりません意味。
あなたはときに、同等のブレークを使用している場合。
IStepBuilder<TData, TStep> End<TStep>(string name) where TStep : IStepBody;
使用例
builder
.StartWith<RandomOutput>(x => x.Name("Random Step"))
.When(0)
.Then<TaskA>()
.Then<TaskB>()
.End<RandomOutput>("Random Step")
.When(1)
.Then<TaskC>()
.Then<TaskD>()
.End<RandomOutput>("Random Step");
2、CancelCondition
条件の下では途中でこのステップの実行を中止します。
contiuneと同等でなければなりません。
/// Prematurely cancel the execution of this step on a condition
IStepBuilder<TData, TStepBody> CancelCondition(Expression<Func<TData, bool>> cancelCondition, bool proceedAfterCancel = false);
またはマルチスレッドの非同期ノード
1、ディレイ
現在のノードが実行を遅延させるように、実行を遅らせました。現在実行中のワークフローをブロックしていません。遅延は、このノードが実行を拡張ノードを追いました。非同期として理解することができ、ワークフローは、このノードが直接次のノード/ステップを実行するのを待たずに終了します。
/// Wait for a specified period
IStepBuilder<TData, Delay> Delay(Expression<Func<TData, TimeSpan>> period);
2、スケジュール
スケジュール実行。現在のノードの時間は、一定期間の後に行われます。スケジュールは、ワークフローをブロックしません。
スケジュールは、ワークフローは、スケジュールが終了待つ/ステップ直下のノードを実行していない、ノンブロッキングです。
/// Schedule a block of steps to execute in parallel sometime in the future
IContainerStepBuilder<TData, Schedule, TStepBody> Schedule(Expression<Func<TData, TimeSpan>> time);
例
builder
.StartWith(context => Console.WriteLine("Hello"))
.Schedule(data => TimeSpan.FromSeconds(5)).Do(schedule => schedule
.StartWith(context => Console.WriteLine("Doing scheduled tasks"))
)
.Then(context => Console.WriteLine("Doing normal tasks"));
3、の曜日
条件が一致しなくなるまでノードに対して繰り返します。
再発非ブロッキングであり、Rezurワークフローが完了するまで待機しませ、そのまま次のノード/ステップを実行します。
/// Schedule a block of steps to execute in parallel sometime in the future at a recurring interval
IContainerStepBuilder<TData, Recur, TStepBody> Recur(Expression<Func<TData, TimeSpan>> interval, Expression<Func<TData, bool>> until);
トランザクションの操作
例外は、ステップのいくつかは、特定の操作を実行するプロセスの間に発生した場合、それは、データベース内のトランザクションに対応しています。
例えば:
builder
.StartWith(context => Console.WriteLine("Begin"))
.Saga(saga => saga
.StartWith<Task1>()
.CompensateWith<UndoTask1>()
.Then<Task2>()
.CompensateWith<UndoTask2>()
.Then<Task3>()
.CompensateWith<UndoTask3>()
)
.OnError(Models.WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(5))
.Then(context => Console.WriteLine("End"));
1、CompensateWith
このステップ未処理の例外場合は、ステップを取り消し、例外が発生した場合、それが実行されます。
プランBは、ノードとして使用することができます。ノードが問題なくタスクを実行するときに、CompensateWithは実行されません。ノード・エラーが発生した場合、それは一定の要件のCompensateWithに応じて実行します。
/// Undo step if unhandled exception is thrown by this step
IStepBuilder<TData, TStepBody> CompensateWith<TStep>(Action<IStepBuilder<TData, TStep>> stepSetup = null) where TStep : IStepBody;
IStepBuilder<TData, TStepBody> CompensateWith(Func<IStepExecutionContext, ExecutionResult> body);
IStepBuilder<TData, TStepBody> CompensateWith(Action<IStepExecutionContext> body);
2、CompensateWithSequence
このステップ未処理の例外場合は、ステップを取り消し、例外が発生した場合、それが実行されます。CompensateWithとの違いは、前者がアクションでのFunc渡されたパラメータが、あるということです。
CompensateWith
内部実装はCompensateWith
、あるCompensateWith
パッケージ。
/// Undo step if unhandled exception is thrown by this step
IStepBuilder<TData, TStepBody> CompensateWithSequence(Action<IWorkflowBuilder<TData>> builder);
3、ONERROR
トランザクション操作のために、エラーが発生した場合、ロールバック中で述べように時間とを設定します。一般的に佐賀で使用されます。
ONERRORがブロックされています。
/// Configure the behavior when this step throws an unhandled exception
IStepBuilder<TData, TStepBody> OnError(WorkflowErrorHandling behavior, TimeSpan? retryInterval = null);
ONERRORノードが異常であり、ロールバック操作、容器内に捕捉することができます。もし代わりに、直接ロールバックが発生する可能性がコンテナノード上で、次いで次のノード。コンテナの役割なら、それは、コンテナが再実行される一連の操作をさせることができます。
あなたが一緒にピアコンテナが、ときONERRORを使用することができますが、自分自身の循環機能と、コードは、論理トランザクションが奇妙になり使用されます。
佐賀には、サイクルは、単純なバッグは、ノードのコンテナであり、それ自体ではない、条件付きではありません。こうして容器サーガトランザクション操作、非常に適した、ロールバック、およびリトライ一連の動作として使用。
第四に、条件またはスイッチ
イテレーション
1、のForEach
反復は、それはそのサイクルを言うことができます。達成することがIEnumerableを内部的に使用します。
違いは、C#は、反復データのために使用され、C#のForeachです。
foreachのワークフローは、要素の数、識別されるべきループの数を決定するために用いることができます。
ForEachがブロックされています。
/// Execute a block of steps, once for each item in a collection in a parallel foreach
IContainerStepBuilder<TData, Foreach, Foreach> ForEach(Expression<Func<TData, IEnumerable>> collection);
例
builder
.StartWith<SayHello>()
.ForEach(data => new List<int>() { 1, 2, 3, 4 })
.Do(x => x
.StartWith<DisplayContext>()
.Input(step => step.Item, (data, context) => context.Item)
.Then<DoSomething>())
.Then<SayGoodbye>();
最終意志サイクル5回。
条件付きの
1、とき
条件付き、条件が真です。
それがブロックされた場合。
場合、ノードは、データ転送(非TDATA)を捕捉することができます。
/// Configure an outcome for this step, then wire it to another step
[Obsolete]
IStepOutcomeBuilder<TData> When(object outcomeValue, string label = null);
/// Configure an outcome for this step, then wire it to a sequence
IContainerStepBuilder<TData, When, OutcomeSwitch> When(Expression<Func<TData, object>> outcomeValue, string label = null);
例えば従来の方法
When(0)
、キャプチャreturn ExecutionResult.Outcome(value);
値を、それが等しいか否かが判定されます。しかし、このアプローチは時代遅れです。
あなたは判断する表現を使用する必要があります。例えば
.When(data => 1)
.When(data => data.value==1)
2、一方で
条件付き、条件が真です。そして、違いがある場合、あなたはキャプチャすることができた場合ExecutionResult.Outcome(value);
。
それがブロックされている間。
/// Repeat a block of steps until a condition becomes true
IContainerStepBuilder<TData, While, While> While(Expression<Func<TData, bool>> condition);
例
builder
.StartWith<SayHello>()
.While(data => data.Counter < 3)
.Do(x => x
.StartWith<DoSomething>()
.Then<IncrementStep>()
.Input(step => step.Value1, data => data.Counter)
.Output(data => data.Counter, step => step.Value2))
.Then<SayGoodbye>();
3、もし
条件の裁判官の資格。
それがブロックされている場合。
/// Execute a block of steps if a condition is true
IContainerStepBuilder<TData, If, If> If(Expression<Func<TData, bool>> condition);
差場合、式が真であれば条件が真であるかどうかが、場合は、ときに、されています。
本質的には、それは関係なく、コードのロジックの、言語の違いです。
本物の場合は/一方で、条件式は、IFによって決定されます。
並行ノード
1、パラレル
並列タスク。コンテナとして、あなたが内側にタスクの複数のセットを設定することができ、これらのタスクは、同時に、並行して実行するだろう。
パラレルがブロックされています。
/// Execute multiple blocks of steps in parallel
IParallelStepBuilder<TData, Sequence> Parallel();
例:
.StartWith<SayHello>()
.Parallel()
.Do(then =>
then.StartWith<PrintMessage>()
.Input(step => step.Message, data => "Item 1.1")
.Then<PrintMessage>()
.Input(step => step.Message, data => "Item 1.2"))
.Do(then =>
then.StartWith<PrintMessage>()
.Input(step => step.Message, data => "Item 2.1")
.Then<PrintMessage>()
.Input(step => step.Message, data => "Item 2.2")
.Then<PrintMessage>()
.Input(step => step.Message, data => "Item 2.3"))
.Do(then =>
then.StartWith<PrintMessage>()
.Input(step => step.Message, data => "Item 3.1")
.Then<PrintMessage>()
.Input(step => step.Message, data => "Item 3.2"))
.Join()
.Then<SayGoodbye>();
三つは、3つの並列タスクに代わって、実行します。3個の並列、ドゥ内のコードを実行し、順次実行されます。
ドのPaeallel:
public interface IParallelStepBuilder<TData, TStepBody>
where TStepBody : IStepBody
{
IParallelStepBuilder<TData, TStepBody> Do(Action<IWorkflowBuilder<TData>> builder);
IStepBuilder<TData, Sequence> Join();
}
加えて、メソッドを実行してください、だけでなく、参加するには、場合、一方で、foreachのを、比較しました。
他のタイプの場合、ノードは、直接ノードを構築してください。
、これと並行して、回収作業を行いますが、最終的にタスクノードを構築して実行する必要がありましょう。
V.その他
圧縮する良い、他のコンテンツを見ていない長い書かれました。
そして、送信データ依存性注入
各ステップの依存性注入のためのワークフローのコア支持点。
データの永続性をサポート
ワークフローコアサポートワークフローストレージが再び後でリコールのためのデータベースに組み込まれます。
支持SQL Serverのは、MySQL、SQLiteの、PostgreSQLの、Redisを、MongoDBの、AWS、Azureの、
Elasticsearch、RabbitMQの... ....
動的呼び出しと動的に生成されたワークフローをサポートしています
あなたは、C#のコードを通じてワークフローを構築、またはJSON、YAMLのダイナミックでワークフローを構築することができます。
ビジュアルデザイナーが使用し、タスクの生成ロジックコンフィギュレーションファイル、および[ワークフローコアダイナミクスを使用してワークフローを作成動的に転送することができます。
限られたスペースではなく、それらを繰り返します。
興味がお支払いください注意コアワークフロー:https://github.com/danielgerlag/workflow-core