Dynamics CRM 2011 カスタム ワークフロー アクティビティ

みなさん、こんにちは。

ゴールデンウィークはいかがお過ごしだったでしょうか。

今回は Dynamics CRM 2011 ワークフローのカスタマイズから、カスタム
ワークフロー アクティビティを紹介します。こちらの機能は設置型でのみ
利用可能となりますのでご注意ください。

カスタム ワークフロー アクティビティ

Dynamics CRM 2011 は前製品に引き続きカスタム ワークフロー アクティビティを
サポートします。既定で、ワークフローは、以下のようにステップを提供しています。

image

フローに関しは選択肢がなく、順次処理 (Sequence) ですが、分岐と待機条件を
サポートしています。またアクティビティとしては、レコードの作成、更新や割り当て、
電子メールの送信等をサポートしています。

上位以外の操作を追加できるのか、カスタム ワークフロー アクティビティです。
今回は SDK で提供されているサンプルを活用したいと思います。testnet4activity
サンプルには、さまざまなカスタム ワークフロー アクティビティのサンプルが
収録されていますが、今回はその中の SimpleSdkActivity を見ていきます。

サンプルの中身

SimpleSdkActivity は、取引先企業レコードを作成すると同時に、タスクを作成し
関連を設定します。その後タスクを 2 通りの方法で取得して、最後の更新します。

以下は、SimpleSdkActivity 以外のサンプルは省略してあります。

using System;
using System.Activities;
using System.Collections.ObjectModel;

// Dynamics CRM 2011 SDK のアセンブリ参照
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Workflow;

namespace Microsoft.Crm.Sdk.Samples
{
  public sealed class SimpleSdkActivity : CodeActivity  // Codeアクティビティを継承
  {
    protected override void Execute(CodeActivityContext executionContext) // Execute を上書き
    {
      // 実行コンテキストよりトレースサービスを作成
      ITracingService tracingService = executionContext.GetExtension<ITracingService>();

      // 実行コンテキストよりコンテキスト作成
      IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
      IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
      // コンテキストより CRM 組織サービスを作成
      IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

      // トレースにメッセージを書き込み      
      tracingService.Trace("Creating Account");

      // 取引先企業オブジェクトの作成
      Account entity = new Account();
      // 名前を入力変数より取得して設定
      entity.Name = AccountName.Get<string>(executionContext);
      // レコードの作成
      Guid entityId = service.Create(entity);

      // トレースにメッセージを書き込み
      tracingService.Trace("Account created with Id {0}", entityId.ToString());

      // トレースにメッセージを書き込み
      tracingService.Trace("Create a task for the account");
      // タスクオブジェクトを作成
      Task newTask = new Task(); 
      // 件名を入力変数より取得して設定
      newTask.Subject = TaskSubject.Get<string>(executionContext);
      // 作成済みの取引先企業に関連付け
      newTask.RegardingObjectId = new EntityReference(Account.EntityLogicalName, entityId);

      // レコードの作製
      Guid taskId = service.Create(newTask);

      // トレースにメッセージを書き込み
      tracingService.Trace("Task has been created");

      // トレースにメッセージを書き込み
      tracingService.Trace("Retrieve the task using QueryByAttribute");
      // QueryByAttribute を利用してクエリを作成
      QueryByAttribute query = new QueryByAttribute();
      // クエリの検索対象列に RegardingObjectId を指定
      query.Attributes.AddRange(new string[] { "regardingobjectid" });
      // クエリの取得列に件名を指定
      query.ColumnSet = new ColumnSet(new string[] { "subject" });
      // クエリ対象のエンティティにタスクを指定
      query.EntityName = Task.EntityLogicalName;
      // 検索対象の値として作成した取引先企業の AccountId を指定
      query.Values.AddRange(new object[] { entityId });

      // トレースにメッセージを書き込み
      tracingService.Trace("Executing the Query for entity {0}", query.EntityName);

      // クエリの実行
      RetrieveMultipleRequest request = new RetrieveMultipleRequest();
      request.Query = query;
      Collection<Entity> entityList = ((RetrieveMultipleResponse)service.Execute(request)).EntityCollection.Entities;

      // トレースにメッセージを書き込み
      tracingService.Trace("Executing a WhoAmIRequest");
      // WhoAmIRequest を実行して実行者を特定
      service.Execute(new WhoAmIRequest());

      // クエリの結果が 1 件ではない場合
      if (1 != entityList.Count)
      { 
        // トレースにメッセージを書き込み
        tracingService.Trace("The entity list was too long");
        throw new InvalidPluginExecutionException("Query did not execute correctly");
      }
      else
      { 
        // トレースにメッセージを書き込み
        tracingService.Trace("Casting the Task from RetrieveMultiple to strong type");
        // クエリの結果をタスクに変換
        Task retrievedTask = (Task)entityList[0];

        // 取得したタスクが作成されたものか確認
        if (retrievedTask.ActivityId != taskId)
        {
          throw new InvalidPluginExecutionException("Incorrect task was retrieved");
        }

        // トレースにメッセージを書き込み
        tracingService.Trace("Retrieving the entity from IOrganizationService");

        // Retrieve メソッドを利用してタスクを取得 
        retrievedTask = (Task)service.Retrieve(Task.EntityLogicalName, retrievedTask.Id, new ColumnSet("subject"));
        // 取得したタスクが作成したものか確認
        if (!string.Equals(newTask.Subject, retrievedTask.Subject, StringComparison.Ordinal))
        {
          throw new InvalidPluginExecutionException("Task's subject did not get retrieved correctly");
        }

        // タスクの更新 件名を入力変数より取得 
        retrievedTask.Subject = UpdatedTaskSubject.Get<string>(executionContext);
        service.Update(retrievedTask);
      }
    }

    // 取引先企業の名前用入力変数
    [Input("Account Name")]
    [Default("Test Account: {575A8B41-F8D7-4DCE-B2EA-3FFDE936AB1B}")]
    public InArgument<string> AccountName { get; set; }

    // タスクの件名用入力変数
    [Input("Task Subject")]
    [Default("Task related to account {575A8B41-F8D7-4DCE-B2EA-3FFDE936AB1B}")]
    public InArgument<string> TaskSubject { get; set; }

    // 更新時のタスクの件名用入力変数
    [Input("Updated Task Subject")]
    [Default("UPDATED: Task related to account {575A8B41-F8D7-4DCE-B2EA-3FFDE936AB1B}")]
    public InArgument<string> UpdatedTaskSubject { get; set; }
  } 
}

アセンブリの準備

1. Visual Studio 2010 を開いて、sdk\samplecode\cs\process\customworkflowactivity\
testnet4activity\testnet4activity.sln を開きます。

2. ソリューションエクスプローラーより、TestNet4Activity を右クリックして
プロパティをクリックします。

3. 署名タブをクリックして、アセンブリの署名で新しい署名を作成します。

4. ビルドメニューよりソリューションのビルドを実行します。

これでアセンブリの準備は完了です。

PluginRegistrationTool で登録

次にアセンブリとカスタムアクティビティの登録です。

1. Visual Studio 2010 を開いて、sdk\tools\pluginregistration の
pluginregistrationtool.sln を開きます。

2. F5 を押下して実行します。

3. Create New Connection ボタンをクリックして、Connection Information に
利用する環境情報を入力します。

image

4. Connect をクリックして、Dynamics CRM サーバーに接続します。

5. 組織の一覧から、アセンブリを登録する組織を選択して、Connect を
クリックします。画面右に詳細が表示されます。

6. Register メニューより、Gegister New Assembly をクリックします。

image

7.  sdk\samplecode\cs\process\customworkflowactivity\testnet4activity\
bin\Debug\Microsoft.Crm.QA.Workflow.TestNet4Activity.dll を選択します。

8. 選択が完了すると、Step #2 の画面に、このアセンブリ内にあるカスタム
アクティビティの一覧が表示されます。

image

9. 今回はSimpleSdkActivity だけを利用したいので、Select All のチェック
をはずした後、SimpleSdkActivity のみチェックします。

image

10. あとは既定値のまま、Register Selected をクリックします。

登録情報の編集

既定の名前では分かりにくいので、登録情報を編集します。

1. 登録されたアセンブリを展開して、登録したカスタムアクティビティを選択します。

image

2. 画面一番右側にプロパティのウィンドウがありますので、そちらを
参照します。

image

3. Name と WorkflowActivityGroupName が編集可能で、また
ワークフローエディター上で表示される名前です。以下のように編集します。

image

4. プロパティ画面下の Save ボタンをクリックします。

ワークフロー内での使用

アセンブリを登録することで、ワークフローエディタに認識されます。

1. 設定 | プロセス センター | プロセスをクリックします。

2. 新規をクリックします。

3. 以下のように設定して、OK をクリックします。

image

4. ステップの追加をクリックします。カスタムグループと
その配下にカスタムアクティビティサンプルが登録されています。

image

5. カスタムワークフローアクティビティサンプルを選択します。

6. プロパティの設定をクリックします。コードで作成した入力変数が
表示され、値を既定から変更できるようになっています。動的な値も利用可能です。

image

7. 変更せずに保存して閉じるをクリックして、ワークフリー自体も保存します。

8. アクティブ化をクリックします。

9. ワークプレイス | 取引先担当者より、レコードを 1 件作成します。

10. 作成した取引先担当者を開いて、ナビゲーションのプロセス | ワークフローを
クリックします。ワークフローの実行状況が表示されます。

11. 成功になったことを確認して、取引先企業とタスクがそれぞれ
作成されていることを確認します。

トレース機能

カスタム ワークフロー アクティビティやプラグインには、トレースを出力する
機能があります。コードの中でもトレースの記述があります。このトレースは
処理が失敗した場合に、ワークフローの詳細から確認することが可能です。

以下の手順でワークフローが失敗するように編集して検証してみましょう。

1. Visual Studio 2010 で testnet4activity ソリューションを開きます。

2. SimpleSdkActivity.cs ソースコードに、例外を発生されるコードを追加します。

        // タスクの更新 件名を入力変数より取得 
        retrievedTask.Subject = UpdatedTaskSubject.Get<string>(executionContext);
        service.Update(retrievedTask);
      }
      // エラーを返すように処理を追加
      throw new InvalidPluginExecutionException();
    }

3. ビルド | ソリューションのビルドよりコンパイルします。

4. PluginRegistratinTool を開いて、組織に接続します。

5. 既に登録されているアセンブリを右クリックして、Update をクリックします。

image

6. アセンブリを登録した時の画面が出るので、前回と同じ手順で
登録を行います。更新完了後 IISRESET をします。

以上で更新は終了です。

7. 再度 Dynamics CRM 2011 に戻って取引先担当者を作成します。

8. 実行されたワークフローを開いて、失敗しているか確認します。

image

9. ワークフローの実行レコードを開きます。プログラムしたとおり、例外が
出力されています。

image

10. 詳細をクリックすると、コード内に記述されたトレースの情報を確認できます。

image

このようにトレースの機能を利用して、トレースの出力をしておけば、例外の発生時に
どこで処理が失敗したかのあたりがつくようになります。

出力変数

最後に出力変数の紹介です。入力変数はサンプルにあるとおりですが、この
サンプルに以下の機能を追加します。

- ワークフローで作成した取引先企業を、取引先担当者の所属取引先企業にする

では早速実装してみましょう。

1. Visual Studio 2010 で testnet4activity ソリューションを開きます。

2. SimpleSdkActivity.cs ソースコードの入力変数の下に、出力変数を追加します。

    // 更新時のタスクの件名用入力変数
    [Input("Updated Task Subject")]
    [Default("UPDATED: Task related to account {575A8B41-F8D7-4DCE-B2EA-3FFDE936AB1B}")]
    public InArgument<string> UpdatedTaskSubject { get; set; }

    // 作成した取引先企業を参照として出力
    // 型は参照で利用するため、EntityReference を利用
    [Output("Created Account")]
    [ReferenceTarget("account")]
    public OutArgument<EntityReference> AccountReference { get; set; }

3. 先ほど追加した、例外出力コードをコメントアウトして、変わりに変数に値を
代入するコードを追加します。

// エラーを返すように処理を追加
// throw new InvalidPluginExecutionException();

// 出力変数に値を代入
// 型が EntityReference のため、新しい EntityReference オブジェクト作成
this.AccountReference.Set(executionContext, new EntityReference("account", entityId));

3. ビルド | ソリューションのビルドよりコンパイルします。

4. PluginRegistratinTool を開いて、組織に接続します。

5. 先ほど同様、登録されているアセンブリを右クリックして、Update をクリックします。

6. 更新が完了後 IISRESET を実行し、作成済みのワークフローを開きます。

7. 編集するために、一旦非アクティブにします。非アクティブ化をクリックします。

8. ステップの追加より、レコードの更新を選択します。

9. 更新対象として取引先担当者を選択した状態で、プロパティの設定をクリックします。

10. 所属取引先企業/上司のフィールドを選択します。

11. フォーム アシスタントより、検索にカスタムワークフローアクティビティサンプルと
取引先企業を選択します。これが出力変数になります。

image

12. OK をクリックして、動的な値を代入します。

image

13. 保存して閉じるをクリックします。

14. せっかくなので作成される取引先企業の名前も変更します。
カスタムワークフローアクティビティサンプルのプロパティ設定をクリックします。

15. Account Name 変数の値を削除します。

16. フォーム アシスタントより以下のように設定を選択します。

image

17. OK をクリックして、値を代入したのち、以下のように編集します。

image

18. 保存して閉じるをクリックして、ワークフローをアクティブ化します。

19. 取引先担当者を 1 件作成します。

20. ワークフローが成功して、作成した取引先企業が取引先担当者に
関連付くことを確認します。

image

まとめ

カスタムワークフローアクティビティは Dynamics CRM 4.0 の頃から
利用可能でしたが、トレース機能の提供や容易なメタデータの編集等
使い勝手が向上しています。是非お試しください!

次回は XAML ワークフローのカスタマイズを紹介します。

- Dynamics CRM サポート 中村 憲一郎