環境 :
Visual Studio 2008 (.NET Framework 3.5)
- 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 (AEC) というものについて少し補足をしておきます。(これは、一体何者なのか ?) ワークフローの処理では、状態の永続化、状態の Compensate 処理 (エラー発生時に補正トランザクションを使って戻す処理です。このデモは、セッションの中でもご紹介した通りです) などのため、実行ツリーや現在の実行状態 (State) などを管理しておく必要があります。この管理は、アクティビティごとに何か印のようなものを付けておけば良いだろうと思うかもしれませんが、今回の While、Replicator などの処理のように、アクティビティがクローンされるケースを想像して頂くと単にアクティビティごとに印をつけておくだけでは不充分であることがおわかり頂けるでしょう。こうした場合に備え、WF が内部で使用しているのが Activity Execution Context です。自作の Composite アクティビティ (複合アクティビティ) を作成する場合にも、この AEC を無視して単に子アクティビティを実行 (Execute) すると、こうした点で不都合が起こるので注意してください。(WF の Sequence アクティビティなども、こうした点に配慮して実装されています。)
この 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 のセッションでご紹介したいと意気込んでいます。
Categories: Uncategorized
こんにちは。 毎年同じようなことを書いているようですみません。またまた Tech*Ed でデモの多くを取りこぼしましたので、残り分を順次掲載していきます。(今、翌日のリハーサル待ちです . . .) 以下の順番で掲載していきます。
LikeLike