events
WorkflowInvoker と WorkflowApplication の使用
Windows Workflow Foundation (WF) には、ワークフローを ホストする方法がいくつか用意されています。 WorkflowInvoker は、メソッド呼び出しのようにワークフローを呼び出す簡単な方法を提供し、永続化を使用しないワークフローのみに使用できます。 WorkflowApplication は、ライフサイクル イベントの通知、実行制御、ブックマークの再開、および永続化を含むワークフローを実行するための豊富なモデルを提供します。 WorkflowServiceHost は、メッセージング アクティビティをサポートし、主にワーク フロー サービスと一緒に使用されます。 このトピックでは、WorkflowInvoker と WorkflowApplication を使用したワークフロー ホスティングついて説明します。 WorkflowServiceHost を使用したワークフローのホストの詳細については、「ワークフロー サービス」と「ワークフロー サービスのホストの概要」を参照してください。
WorkflowInvoker は、メソッドを呼び出すようにワークフローを実行するモデルを提供します。 WorkflowInvoker を使用してワークフローを呼び出すには、Invoke メソッドを呼び出し、呼び出すワークフローのワークフロー定義を渡します。 次の例では、WriteLine を使用して WorkflowInvoker アクティビティを呼び出します。
Activity wf = new WriteLine
{
Text = "Hello World."
};
WorkflowInvoker.Invoke(wf);
WorkflowInvoker を使用してワークフローを呼び出すと、呼び出し元のスレッドでワークフローが実行され、ワークフローが完了するまで (アイドル時間を含む) Invoke メソッドがブロックされます。 ワークフローを完了しなければならないタイムアウト期間を構成するには、Invoke パラメーターを受け取る TimeSpan オーバーロードのいずれかを使用します。 この例では、2 つの異なるタイムアウト期間を使用してワークフローを 2 回呼び出します。 最初のワークフローは完了しますが、2 回目は完了しません。
Activity wf = new Sequence()
{
Activities =
{
new WriteLine()
{
Text = "Before the 1 minute delay."
},
new Delay()
{
Duration = TimeSpan.FromMinutes(1)
},
new WriteLine()
{
Text = "After the 1 minute delay."
}
}
};
// This workflow completes successfully.
WorkflowInvoker.Invoke(wf, TimeSpan.FromMinutes(2));
// This workflow does not complete and a TimeoutException
// is thrown.
try
{
WorkflowInvoker.Invoke(wf, TimeSpan.FromSeconds(30));
}
catch (TimeoutException ex)
{
Console.WriteLine(ex.Message);
}
注意
TimeoutException がスローされるのは、タイムアウト期間が経過してワークフローが実行中にアイドル状態になった場合だけです。 指定されたタイムアウト時間内には完了しないワークフローが正常に完了するのは、アイドル状態にならない場合です。
WorkflowInvoker も非同期バージョンのメソッド呼び出しを提供します。 詳細については、次のトピックを参照してください。 InvokeAsync および BeginInvoke
ワークフローの入力引数にマップされ、引数名によってキー指定されている入力パラメーターの辞書を使用して、データをワークフローに渡すことができます。 次の例では、WriteLine が呼び出され、その Text 引数の値は入力パラメーターの辞書を使用して指定されています。
Activity wf = new WriteLine();
Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Text", "Hello World.");
WorkflowInvoker.Invoke(wf, inputs);
ワークフローの出力パラメーターは、Invoke の呼び出しから返される出力辞書を使用して取得できます。 次の例は、2 つの入力引数と 2 つの出力引数を持つ 1 つの Divide
アクティビティで構成されるワークフローを呼び出します。 ワークフローを呼び出すと、arguments
辞書が渡されます。ここには引数名でキー指定された各入力引数の値が含まれています。 Invoke
の呼び出しから制御が戻るときに、同様に引数名でキー指定された各出力引数が、outputs
辞書で返されます。
public sealed class Divide : CodeActivity
{
[RequiredArgument]
public InArgument<int> Dividend { get; set; }
[RequiredArgument]
public InArgument<int> Divisor { get; set; }
public OutArgument<int> Remainder { get; set; }
public OutArgument<int> Result { get; set; }
protected override void Execute(CodeActivityContext context)
{
int quotient = Dividend.Get(context) / Divisor.Get(context);
int remainder = Dividend.Get(context) % Divisor.Get(context);
Result.Set(context, quotient);
Remainder.Set(context, remainder);
}
}
int dividend = 500;
int divisor = 36;
Dictionary<string, object> arguments = new Dictionary<string, object>();
arguments.Add("Dividend", dividend);
arguments.Add("Divisor", divisor);
IDictionary<string, object> outputs =
WorkflowInvoker.Invoke(new Divide(), arguments);
Console.WriteLine("{0} / {1} = {2} Remainder {3}",
dividend, divisor, outputs["Result"], outputs["Remainder"]);
ワークフローが CodeActivity<TResult>
や Activity<TResult>
などの ActivityWithResult から派生し、適切に定義された Result 出力引数以外にも出力引数がある場合は、Invoke
の非ジェネリック オーバーロードを使用して、追加の引数を取得する必要があります。 これを行うには、Invoke
に渡されるワークフロー定義は Activity 型である必要があります。 この例では、Divide
アクティビティは CodeActivity<int>
から派生していますが、Activity として宣言されているため、1 つの戻り値ではなく引数の辞書を返す、Invoke
の非ジェネリック オーバーロードが使用されます。
public sealed class Divide : CodeActivity<int>
{
public InArgument<int> Dividend { get; set; }
public InArgument<int> Divisor { get; set; }
public OutArgument<int> Remainder { get; set; }
protected override int Execute(CodeActivityContext context)
{
int quotient = Dividend.Get(context) / Divisor.Get(context);
int remainder = Dividend.Get(context) % Divisor.Get(context);
Remainder.Set(context, remainder);
return quotient;
}
}
int dividend = 500;
int divisor = 36;
Dictionary<string, object> arguments = new Dictionary<string, object>();
arguments.Add("Dividend", dividend);
arguments.Add("Divisor", divisor);
Activity wf = new Divide();
IDictionary<string, object> outputs =
WorkflowInvoker.Invoke(wf, arguments);
Console.WriteLine("{0} / {1} = {2} Remainder {3}",
dividend, divisor, outputs["Result"], outputs["Remainder"]);
WorkflowApplication は、ワークフロー インスタンス管理を実現する豊富な機能を備えています。 WorkflowApplication は実際の WorkflowInstance に対してスレッド セーフなプロキシとして動作し、ランタイムをカプセル化します。また、ワークフロー インスタンスの作成と読み込み、ライフサイクル イベントの一時停止と再開、終了、および通知を行うメソッドを提供します。 WorkflowApplication を使用してワークフローを実行するには、WorkflowApplication を作成して必要なライフサイクル イベントに定期受信し、ワークフローを開始して、それが終了するまで待機します。 この例では、WriteLine アクティビティから成るワークフロー定義が作成され、そのワークフロー定義を使用して WorkflowApplication が作成されます。 ワーク フローが完了したときにホストに通知されるように Completed が処理され、Run の呼び出しによってワーク フローが開始され、ホストはワークフローが完了するのを待ちます。 次の例に示すように、ワークフローが完了すると AutoResetEvent が設定され、ホスト アプリケーションは実行を再開できます。
AutoResetEvent syncEvent = new AutoResetEvent(false);
Activity wf = new WriteLine
{
Text = "Hello World."
};
// Create the WorkflowApplication using the desired
// workflow definition.
WorkflowApplication wfApp = new WorkflowApplication(wf);
// Handle the desired lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
syncEvent.Set();
};
// Start the workflow.
wfApp.Run();
// Wait for Completed to arrive and signal that
// the workflow is complete.
syncEvent.WaitOne();
Completed の他にも、ワークフローがアンロードされたとき (Unloaded)、中止されたとき (Aborted)、アイドル状態になったとき (Idle および PersistableIdle)、または未処理の例外が発生したとき (OnUnhandledException) にホストの作成者に通知されます。 次の例に示すように、ワークフロー アプリケーションの開発者はこれらの通知を処理して適切なアクションを行うことができます。
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
if (e.CompletionState == ActivityInstanceState.Faulted)
{
Console.WriteLine("Workflow {0} Terminated.", e.InstanceId);
Console.WriteLine("Exception: {0}\n{1}",
e.TerminationException.GetType().FullName,
e.TerminationException.Message);
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
}
else
{
Console.WriteLine("Workflow {0} Completed.", e.InstanceId);
// Outputs can be retrieved from the Outputs dictionary,
// keyed by argument name.
// Console.WriteLine("The winner is {0}.", e.Outputs["Winner"]);
}
};
wfApp.Aborted = delegate (WorkflowApplicationAbortedEventArgs e)
{
// Display the exception that caused the workflow
// to abort.
Console.WriteLine("Workflow {0} Aborted.", e.InstanceId);
Console.WriteLine("Exception: {0}\n{1}",
e.Reason.GetType().FullName,
e.Reason.Message);
};
wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
// Perform any processing that should occur
// when a workflow goes idle. If the workflow can persist,
// both Idle and PersistableIdle are called in that order.
Console.WriteLine("Workflow {0} Idle.", e.InstanceId);
};
wfApp.PersistableIdle = delegate (WorkflowApplicationIdleEventArgs e)
{
// Instruct the runtime to persist and unload the workflow.
// Choices are None, Persist, and Unload.
return PersistableIdleAction.Unload;
};
wfApp.Unloaded = delegate (WorkflowApplicationEventArgs e)
{
Console.WriteLine("Workflow {0} Unloaded.", e.InstanceId);
};
wfApp.OnUnhandledException = delegate (WorkflowApplicationUnhandledExceptionEventArgs e)
{
// Display the unhandled exception.
Console.WriteLine("OnUnhandledException in Workflow {0}\n{1}",
e.InstanceId, e.UnhandledException.Message);
Console.WriteLine("ExceptionSource: {0} - {1}",
e.ExceptionSource.DisplayName, e.ExceptionSourceInstanceId);
// Instruct the runtime to terminate the workflow.
// Other choices are Abort and Cancel. Terminate
// is the default if no OnUnhandledException handler
// is present.
return UnhandledExceptionAction.Terminate;
};
WorkflowInvoker を使用しているときにデータを渡す方法と同様に、起動時にパラメーターの辞書を使用してデータをワークフローに渡すことができます。 辞書の各項目は、指定されたワークフローの入力引数にマップされます。 次の例では、WriteLine アクティビティから成るワークフローが呼び出され、その Text 引数が入力パラメーターの辞書を使用して指定されます。
AutoResetEvent syncEvent = new AutoResetEvent(false);
Activity wf = new WriteLine();
// Create the dictionary of input parameters.
Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Text", "Hello World!");
// Create the WorkflowApplication using the desired
// workflow definition and dictionary of input parameters.
WorkflowApplication wfApp = new WorkflowApplication(wf, inputs);
// Handle the desired lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
syncEvent.Set();
};
// Start the workflow.
wfApp.Run();
// Wait for Completed to arrive and signal that
// the workflow is complete.
syncEvent.WaitOne();
ワークフローが完了したら、WorkflowApplicationCompletedEventArgs.Outputs 辞書にアクセスして、Completed ハンドラーの出力引数を取得できます。 次の例では、WorkflowApplication を使用してワークフローをホストしています。 1 つの DiceRoll
アクティビティで構成されるワークフロー定義を使用して WorkflowApplication インスタンスが構築されます。 DiceRoll
アクティビティには、サイコロ振り操作の結果を表す 2 つの出力引数があります。 ワークフローが完了すると、この出力が Completed ハンドラーで取得されます。
public sealed class DiceRoll : CodeActivity
{
public OutArgument<int> D1 { get; set; }
public OutArgument<int> D2 { get; set; }
static Random r = new Random();
protected override void Execute(CodeActivityContext context)
{
D1.Set(context, r.Next(1, 7));
D2.Set(context, r.Next(1, 7));
}
}
// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(new DiceRoll());
// Subscribe to any desired workflow lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
if (e.CompletionState == ActivityInstanceState.Faulted)
{
Console.WriteLine("Workflow {0} Terminated.", e.InstanceId);
Console.WriteLine("Exception: {0}\n{1}",
e.TerminationException.GetType().FullName,
e.TerminationException.Message);
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
}
else
{
Console.WriteLine("Workflow {0} Completed.", e.InstanceId);
// Outputs can be retrieved from the Outputs dictionary,
// keyed by argument name.
Console.WriteLine("The two dice are {0} and {1}.",
e.Outputs["D1"], e.Outputs["D2"]);
}
};
// Run the workflow.
wfApp.Run();
注意
WorkflowApplication と WorkflowInvoker は入力引数の辞書を取得し、out
引数の辞書を返します。 これらの辞書のパラメーター、プロパティ、および戻り値は IDictionary<string, object>
型です。 渡される辞書クラスの実際のインスタンスには、IDictionary<string, object>
を実装した任意のクラスを使用できます。 これらの例では、Dictionary<string, object>
が使用されています。 ディクショナリの詳細については、IDictionary<TKey,TValue> と Dictionary<TKey,TValue> に関するそれぞれのページを参照してください。
ブックマークは、アクティビティが再開されるのを受動的に待機するメカニズムです。また、実行中のワークフロー インスタンスにデータを渡すメカニズムでもあります。 次の例に示すように、アクティビティがデータを待機している場合、Bookmark を作成し、Bookmark が再開されたときに呼び出されるコールバック メソッドを登録します。
public sealed class ReadLine : NativeActivity<string>
{
[RequiredArgument]
public InArgument<string> BookmarkName { get; set; }
protected override void Execute(NativeActivityContext context)
{
// Create a Bookmark and wait for it to be resumed.
context.CreateBookmark(BookmarkName.Get(context),
new BookmarkCallback(OnResumeBookmark));
}
// NativeActivity derived activities that do asynchronous operations by calling
// one of the CreateBookmark overloads defined on System.Activities.NativeActivityContext
// must override the CanInduceIdle property and return true.
protected override bool CanInduceIdle
{
get { return true; }
}
public void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object obj)
{
// When the Bookmark is resumed, assign its value to
// the Result argument.
Result.Set(context, (string)obj);
}
実行されると、ReadLine
アクティビティは Bookmark を作成し、コールバックを登録してから、Bookmark が再開されるのを待機します。 再開されると、ReadLine
アクティビティは Bookmark と一緒に渡されたデータを Result 引数に代入します。 次の例では、ReadLine
アクティビティを使用してユーザー名を収集し、それをコンソール ウィンドウに表示するワークフローを作成します。
Variable<string> name = new Variable<string>();
Activity wf = new Sequence
{
Variables = { name },
Activities =
{
new WriteLine
{
Text = "What is your name?"
},
new ReadLine
{
BookmarkName = "UserName",
Result = new OutArgument<string>(name)
},
new WriteLine
{
Text = new InArgument<string>((env) =>
("Hello, " + name.Get(env)))
}
}
};
// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);
// Workflow lifecycle events omitted except idle.
AutoResetEvent idleEvent = new AutoResetEvent(false);
wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
idleEvent.Set();
};
// Run the workflow.
wfApp.Run();
// Wait for the workflow to go idle before gathering
// the user's input.
idleEvent.WaitOne();
// Gather the user's input and resume the bookmark.
// Bookmark resumption only occurs when the workflow
// is idle. If a call to ResumeBookmark is made and the workflow
// is not idle, ResumeBookmark blocks until the workflow becomes
// idle before resuming the bookmark.
BookmarkResumptionResult result = wfApp.ResumeBookmark("UserName",
Console.ReadLine());
// Possible BookmarkResumptionResult values:
// Success, NotFound, or NotReady
Console.WriteLine("BookmarkResumptionResult: {0}", result);
ReadLine
アクティビティが実行されると、Bookmark という名前の UserName
が作成され、ブックマークが再開されるのを待機します。 ホストは必要なデータを収集し、Bookmark を再開します。 ワークフローが再開されると名前が表示されて、完了します。
ホスト アプリケーションでは、ワークフローを調べて、アクティブなブックマークがあるかどうかを確認できます。 この操作を実行するには、GetBookmarks インスタンスの WorkflowApplication メソッドを呼び出すか、WorkflowApplicationIdleEventArgs ハンドラーの Idle を調べます。
次のコード例は前の例と似ていますが、ブックマークを再開する前にアクティブなブックマークを列挙する点が異なります。 ワークフローが開始され、Bookmark が作成されてワークフローがアイドル状態になると、GetBookmarks が呼び出されます。 このワークフローが完了すると、次の出力がコンソールに表示されます。
お名前は何ですか。
BookmarkName: UserName - OwnerDisplayName: ReadLineSteveHello, Steve
Variable<string> name = new Variable<string>();
Activity wf = new Sequence
{
Variables = { name },
Activities =
{
new WriteLine
{
Text = "What is your name?"
},
new ReadLine
{
BookmarkName = "UserName",
Result = new OutArgument<string>(name)
},
new WriteLine
{
Text = new InArgument<string>((env) =>
("Hello, " + name.Get(env)))
}
}
};
// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);
// Workflow lifecycle events omitted except idle.
AutoResetEvent idleEvent = new AutoResetEvent(false);
wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
// You can also inspect the bookmarks from the Idle handler
// using e.Bookmarks
idleEvent.Set();
};
// Run the workflow.
wfApp.Run();
// Wait for the workflow to go idle and give it a chance
// to create the Bookmark.
idleEvent.WaitOne();
// Inspect the bookmarks
foreach (BookmarkInfo info in wfApp.GetBookmarks())
{
Console.WriteLine("BookmarkName: {0} - OwnerDisplayName: {1}",
info.BookmarkName, info.OwnerDisplayName);
}
// Gather the user's input and resume the bookmark.
wfApp.ResumeBookmark("UserName", Console.ReadLine());
WorkflowApplicationIdleEventArgs インスタンスの Idle ハンドラーに渡される WorkflowApplication を調べるコード サンプルを次に示します。 この例では、アイドル状態になるワークフローに、Bookmark という名前で EnterGuess
というアクティビティによって所有されている 1 つの ReadInt
があります。 このコード例は、「チュートリアル入門」の一部である「方法: ワークフローを実行する」に基づいています。 この例のコードを含めるようにこの手順の Idle ハンドラーを変更すると、次の出力が表示されます。
BookmarkName: EnterGuess - OwnerDisplayName: ReadInt
wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
foreach (BookmarkInfo info in e.Bookmarks)
{
Console.WriteLine("BookmarkName: {0} - OwnerDisplayName: {1}",
info.BookmarkName, info.OwnerDisplayName);
}
idleEvent.Set();
};
WorkflowInvoker はワークフローを呼び出す簡単な方法を提供します。また、ワークフローの開始時にデータを渡し、完了したワークフローからデータを抽出する方法を提供しますが、WorkflowApplication を使用できるような複雑なシナリオには使用できません。
.NET に関するフィードバック
.NET はオープンソース プロジェクトです。 フィードバックを提供するにはリンクを選択します。
その他のリソース
トレーニング
ラーニング パス
Use advance techniques in canvas apps to perform custom updates and optimization - Training
Use advance techniques in canvas apps to perform custom updates and optimization
ドキュメント
-
命令型コードを使用してワークフロー、アクティビティ、および式を作成する方法 - .NET Framework
Workflow Foundation ワークフロー定義は、構成済みのアクティビティ オブジェクトのツリーです。 コードを使用して、ワークフロー定義、アクティビティ、および式を作成します。
-
アクティビティの使用と作成 - .NET Framework
詳細情報: アクティビティの使用と作成
-
カスタム アクティビティの設計と実装 - .NET Framework
この記事では、複合アクティビティを作成するか新しいアクティビティの種類を作成することによって Workflow Foundation でカスタム アクティビティを作成するためのリソースを提供します。