(Part 5) SharePoint ワークフロー : 上級者のための IListItemService の活用

※ 以下、Tech ED T-405 セッションに関するフォローアップ記事 Part 5 です。 

  1. SharePoint Designer のカスタムアクティビティの開発で workflowProperties と同等の変数を使うには 
  2. SharePoint ワークフローで Modification Form (修正フォーム) を開発する際のいくつかのポイント
  3. Replicator を使いこなす (実行時にワークフローの流れを変える)
  4. SharePoint ワークフローにおけるカスタムなワークフローステータスの設定
  5. SharePoint ワークフロー : 上級者のための IListItemService の活用
  6. SharePoint の Workflow 用 Web Service を使ってシステム連携をおこなう

環境:
Microsoft Office SharePoint Server 2007
Visual Studio 2005 (及び Workflow 用の extensions)
Office SharePoint Server 2007 SDK

こんにちは。

セッションの中でドキュメントドリブンなワークフローについて説明しました。Open XML と組み合わせることで、セッションでお見せしたように、タスクをいっさい媒介させずにドキュメントの更新のみでフローを進めていくことが可能になります (ただし、デモでは Word のコンテンツコントロールを使っていたのですが、Excel など他の Office 製品の場合には、内部の XML データとビューをバインドさせるコントロールがないので、VBA などでバインドのためのコードを実装する必要があります)。さらに、以前、私のブログでも記載した RoleAssignments と併用してドキュメントのアクセス権(ドキュメントのエントリそのものを見せないようにしたり、閲覧権限のみにしたり、など)を設定すれば、ドキュメントのみで制御を完結した SharePoint ワークフローを作成することができます。

デモでお見せしたような単一のリストとやりとりするなら既存で提供されている OnWorkflowItemChanged アクティビティだけでも充分ですが、ご説明したように、ドキュメント系の処理では、IListItemService というデータ交換用のサービスを使うことでもっと幅広いアイテム連携のシナリオで使えるようになっています。製品開発などコアな機能の実装をお考えの方は、面倒でも使用する価値のあるインタフェース (データ交換サービス用のインタフェース) です。

さて、ここまではセッションでご説明していた内容ですが、IListItemService には、実は、もう 1 つの使い道があります。それが、セッションの冒頭でご説明していたスレッドの解決です。
ご説明したように、SharePoint では、SQL Server の同時実行制御やロックとは別に、SharePoint 用の制御が実装されています。こうした処理が、SharePoint のワークフローインスタンスという「別スレッド」から実行された場合、通常では発生しないエラー(競合)が発生する場合があります。こうした場合、ご紹介した SystemUpdate などの代替メソッドによる回避以外にも、今回ご紹介するように、WF の「データ交換サービス」について知識のある方ならお分かり頂けると思いますが、データ交換サービスが提供するメソッドを実行するとその処理はワークフローインスタンス側ではなくホスト側 (SharePoint側) に渡され SharePoint のホストがこの処理を実行するため、スレッドの問題を回避できることがあります。
分かりやすい例だと、チェックイン(CheckIn) / チェックアウト(CheckOut) があります。これらは、SharePoint 上で特別な制御 (ロックなど) の仕組みを実行しているため、もともと別スレッドで実行すると例外などが発生してしまうはずです。こうした場合にも、IListItemService が提供するチェックイン / チェックアウトを呼び出すことで解決されることでしょう。IListItemService には、以下のメソッドが提供されています。

CheckInListItem
CheckOutlistItem
CopyListItem
CreateDocument
CreateListItem
DeleteListItem
UndoCheckOutListItem
UpdateDocument
UpdateListItem

では、さいごに、この IListItemService を使った実装例を掲載しておきます。(ここでは、例によってサンプルとしての簡潔性を重視するため、単にリストアイテムの更新イベントを処理するためのアクティビティを作成しましょう。)

ご説明したように、これは上級者向けのインタフェースで、ツールボックスには関連するアクティビティが入っていません。よって、開発者みずからが CallExternalMethod や HandleExternalEvent を継承してアクティビティを作成します。 

まず、Visual Studio で、WF の [Workflow Activity Library] のプロジェクトを作成します。IListItemService を使えるようにするため、[参照の追加] で、以下の dll への参照を追加しておきましょう。

%programfiles%Common FilesMicrosoft Sharedweb server extensions12ISAPIMicrosoft.SharePoint.dll
%programfiles%Common FilesMicrosoft Sharedweb server extensions12ISAPIMicrosoft.SharePoint.WorkflowActions.dll

さて、イベント処理用のアクティビティの作成ですが、さきほど作成したアクティビティのプロジェクトで、アクティビティの継承元を SequenceActivity からHandleExternalEventActivity に変更しておきます。
さらに、デザイナーを表示し、プロパティ設定で、InterfaceType から Microsoft.SharePoint.Workflow.IListItemService インタフェースを選択し、EventName から OnItemChanged を選択します。(この辺りは、通常のデータ交換サービスの設定と同様です。)
イベントアクティビティは、これだけでOKです。

さて、IListItemService の中のイベントハンドラを使用する際に、IListItemService の中のメソッド InitializeForEvent を事前に呼び出しておく必要があります。(あらかじめこれを呼び出してどのリストを対象とするか、など、メタ情報を渡しておかないとイベントが扱えません。)
そこで、以下では、この InitializeForEvent を呼び出すために、CallExternalMethodActivity を継承してこのためのアクティビティの作成もおこなっていきます (無論、どこかのアクティビティの中に埋め込んで呼び出しても構いませんが、今回は、このように、アクティビティとして分離しておきます)。

上述のプロジェクトに、[追加] – [新しい項目] で、新たにアクティビティを追加します。今度は、継承元を CallExternalMethodActivity にしておきましょう。

InitializeForEvent メソッドでは、パラメータとして、id, itemId, listId を渡す必要があります。これにより、どのリストのどのアイテムの変更イベントを受け取るか指定します。しかし、この値は、実際にはアクティビティ内部では判断不可能で、アクティビティを使うワークフローがこの値を知っているはずです。
よって、これらは、このアクティビティを使う開発者がデザイナーから設定できるように DependencyProperty としておき、MethodInvoking ハンドラ (Meyhod を呼び出す前に呼ばれるハンドラです) の中で渡された値をそのままメソッドに渡しておきましょう。
すると、コードは、以下のようになるでしょう。

public partial class InitializeForDocumentEvent: CallExternalMethodActivity
{
 public InitializeForDocumentEvent()
 {
  InitializeComponent();
 }

    public static DependencyProperty ListIdProperty = DependencyProperty.Register(“ListId”, typeof(Guid), typeof(InitializeForDocumentEvent));
    [ValidationOption(ValidationOption.Required)]
    public Guid ListId
    {
        get
        {
            return (Guid)base.GetValue(ListIdProperty);
        }
        set
        {
            base.SetValue(ListIdProperty, value);
        }
    }

    public static DependencyProperty ItemIdProperty = DependencyProperty.Register(“ItemId”, typeof(int), typeof(InitializeForDocumentEvent));
    [ValidationOption(ValidationOption.Required)]
    public int ItemId
    {
        get
        {
            return (int)base.GetValue(ItemIdProperty);
        }
        set
        {
            base.SetValue(ItemIdProperty, value);
        }
    }

    public static DependencyProperty IdProperty = DependencyProperty.Register(“Id”, typeof(Guid), typeof(InitializeForDocumentEvent));
    public Guid Id
    {
        get
        {
            return (Guid)base.GetValue(IdProperty);
        }
        set
        {
            base.SetValue(IdProperty, value);
        }
    }

    private void InitializeForDocumentEvent_MethodInvoking(object sender, EventArgs e)
    {
        base.OnMethodInvoking(e);
        this.ParameterBindings[“id”].Value = this.Id;
        this.ParameterBindings[“listId”].Value = this.ListId;
        this.ParameterBindings[“itemId”].Value = this.ItemId;
    }
}

最後に、このライブラリへ署名し、ビルドをおこなって、GAC に登録します。

あとは、上記のアクティビティを使用するワークフローを作成して終了です。上記で作成した InitializeForDocumentEvent アクティビティには、ListId プロパティなど、先ほど公開しておいたプロパティをワークフロー側から設定して使用しましょう。(ワークフローと同一のリストの同一のアイテムの場合には、ListId プロパティとして workflowProperties.ListId を設定し、ItemId プロパティには workflowProperties.Item.ID を設定しておくと通常のアイテム更新イベントのハンドラと同じものとして使うこともできます。なお、この 2 つさえ設定しておけば Id プロパティは必要ありませんので、そのままで結構です。)

Advertisements

3 thoughts on “(Part 5) SharePoint ワークフロー : 上級者のための IListItemService の活用

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s