[T2-302 follow-up (3)] WF の Activity Execution Context (AEC) とクローンに要注意
- WCF の ASP.NET Compatibility Model を使用した Stateful な N-tier アプリケーション (WCF による Load Balancing 全般の考察)
- WCF トレースの見方
- WF の Activity Execution Context (AEC) とクローンに要注意
こんにちは。
(1) で記載しました通り、本日できなかったデモのフォローアップを記載します。
T2-302 のセッションでは、全般に、スケーラビリティ、パフォーマンスなどのエンタープライズ開発のネタを中心にまとめましたが、これだけ異質なテーマだと思われるかもしれません。
実は、この内容については、大変よくご質問を受けますので、あえて Tips 的な内容ですがセッションに含めました。(しかし、これもまったく説明の時間がありませんでした . . .すみません)
まず、くどくどと説明をおこなう前に、以下の簡単な WF のサンプルをご覧ください。

While の条件 (コード条件としています) と、各 CodeActivity では、以下のような処理をおこなっています。
public int CalcValue = 0;
public int whileCount = 0;
private void LoopCheck(object sender, ConditionalEventArgs e)
{
whileCount++;
e.Result = (whileCount <= 2);
}
private void initReplicator_ExecuteCode(object sender, EventArgs e)
{
List<string> initialChildData = new List<string>();
for (int i = 0; i < whileCount; i++)
initialChildData.Add("");
this.replicatorActivity1.InitialChildData = initialChildData;
}
private void addActivity_ExecuteCode(object sender, EventArgs e)
{
CalcValue += 1;
}
private void outputResult_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("The answer is {0}.", CalcValue);
}
処理の内容を簡単にご説明すると、While が 2 回まわって、Replicator アクティビティにより、While の回数分 (例えば、2 回目のループでは 2 個分) の直列 (Sequential) な処理が生成され、その処理の中では数字が 1 足されます。
よって、1 回目のループで 1 足し、2 回目のループで 2 足して終了となりますので、答えは 3 になることが期待されます。
しかし、結論は 1 になります。
「これはなぜか ?」 というのが、このテーマになります。
実は、今回のように、While や Replicator といったような、内部のアクティビティを複数回実行するようなアクティビティでは、内部でアクティビティのクローンが生成されています。
よって、initReplicator アクティビティの中の以下のコードの部分で replicatorActivity1 としているアクティビティが、クローンされた各アクティビティのどのインスタンスを指しているか曖昧になっているのがおわかり頂けるでしょう。
private void initReplicator_ExecuteCode(object sender, EventArgs e)
{
List<string> initialChildData = new List<string>();
for (int i = 0; i < whileCount; i++)
initialChildData.Add("");
this.replicatorActivity1.InitialChildData = initialChildData;
}
みなさんがコードアクティビティで処理を記述すると、それらは、ルートのワークフロー (ルートのワークフローも Activity です) の中に生成されます。よって、この中で、 上記のように変数名の指定や、GetActivityByName メソッドで普通にアクティビティを取得すると、ルートアクティビティの Activity Execution Context が検索され、クローンされる前のテンプレートに相当するアクティビティが取得されます。よって、上記のコードでは、常に最初のループ (1 回目のループ) で使用される Replicator アクティビティが取得される結果となり、 2 回目以降では Replicate されず、処理は 1 回しかまわらないという結果になっていたわけです。
この Activity Execution Context とクローンの関係を図示したものが、本日みなさんにお渡ししたスライドの以下になります。

よって、これを修正するには、1 例ですが (他にも方法はありますが)、現在の Activity の親を取得して、同一の Activity Execution Context 内部で GetActivityByName メソッドなどにより所定のアクティビティを取得するというのが正解になります。
修正したコードは以下になります。
private void initReplicator_ExecuteCode(object sender, EventArgs e)
{
List<string> initialChildData = new List<string>();
for (int i = 0; i < whileCount; i++)
initialChildData.Add("");
Activity myActivity = (Activity)sender;
SequenceActivity parentActivity = (SequenceActivity)myActivity.Parent;
ReplicatorActivity replicatorActivity = (ReplicatorActivity)parentActivity.GetActivityByName("replicatorActivity1");
replicatorActivity.InitialChildData = initialChildData;
}
冒頭でも記載しましたが、この落とし穴は WF を本格的に使用しはじめると結構皆さんはまるようですので、常に意識して実装をおこなってください。
あと、T2-302 セッションでは WCF LOB Adapter 系のデモが 0 という悲惨な状況でしたが、これについては明日の T2-401 のセッションでご紹介したいと意気込んでいます。