オブジェクト変更追跡とオプティミスティック同時実行制御

最終更新日: 2010年3月4日

適用対象: SharePoint Foundation 2010

この記事の内容
エンティティの識別情報
オプティミスティック同時実行制御と不一致
重要なイベント、メソッド、およびプロパティ

リスト アイテム、およびリスト アイテムの特定のフィールドを追加、削除、または編集するには、LINQ to SharePoint プロバイダーを使用します。このプロバイダーは、コンテンツ データベース内のエンティティを表すオブジェクトに対してコードで行われた変更を追跡します。また、コードで取得されたエンティティが、その後で他のユーザーによって変更されているかどうかを、変更を書き込む前に確認します。このトピックでは、オブジェクト変更追跡のシステムについて説明します。Microsoft SharePoint Foundation のデータを変更する方法の詳細については、「How to: Write to the Content Databases Using LINQ to SharePoint」を参照してください。

エンティティの識別情報

LINQ to SharePoint プロバイダーは、クエリによって返されるすべてのエンティティと、それらのエンティティに対するすべての変更を追跡します。指定したエンティティが 2 回以上返された場合、プロバイダーは、常に最初に返したエンティティの同じインスタンスを返します。この動作によって、アプリケーションでは指定したエンティティの同じインスタンスが常に処理されるようになり、別のアプリケーションで変更されたエンティティが処理されることはなくなります。

オプティミスティック同時実行制御と不一致

DataContext.SubmitChanges() の実行時に、データベース内のエンティティの現在の状態と、LINQ to SharePoint プロバイダーが最初にそのエンティティを返したとき (DataContext.SubmitChanges() メソッドの最後の呼び出しの後) のエンティティの状態が比較されます。不一致があれば、最初の取得後に別のアプリケーションによってエンティティが変更されていることになります。アプリケーション開発者は SubmitChanges() の動作を構成し、最初の不一致が検出されたときにそれ以上の変更の書き込みを中止することも、データベースへの変更の書き込みを続行し、検出された競合を記録するように設定することもできます。競合に関係しているフィールドに影響する変更は、データベースにコミットされません。DataContext.SubmitChanges() メソッドを再度呼び出す前に、呼び出し元コードで不一致を解決する必要があります。オプティミスティック同時実行制御のシナリオでは、通常、不一致を解決するために、不一致に関する情報をユーザーに表示し、変更を取り消して他のユーザーの変更を残すか、他のユーザーの変更を上書きするかをユーザーが選択できるようにします。

重要なイベント、メソッド、およびプロパティ

以下のセクションでは、コードでオブジェクト変更追跡システムを活用し、オプティミスティック同時実行制御によるコンテンツ データベースへの書き込みを実装するための主なクラスとメンバーの概要を示します。

エンティティの状態と元の値の追跡

エンティティが変更されたかどうか、およびどのように変更されたかに関するエンティティの状態は、そのエンティティを表すクラスの EntityState プロパティに格納されます。このプロパティは ITrackEntityState インターフェイスで宣言され、オブジェクト変更追跡システムに含めるエンティティを表すすべてのクラスでこのインターフェイスを実装する必要があります。一般には、コンテンツ タイプを表すクラスで ITrackEntityState インターフェイスを実装します。そうしたクラスの各オブジェクトは特定のリスト アイテムを表しており、リスト アイテムのフィールドを表すプロパティがクラスにあるからです。

同時実行の競合が検出された場合は、コードで変更を取り消すことが必要になる場合があるので、それらのコンテンツ タイプのクラスにフィールドの元に値も格納する必要があります。これを行うには、クラスで ITrackOriginalValues インターフェイスを実装します。このインターフェイスでは、プロパティの名前と値の辞書となる OriginalValues プロパティが定義されます。クラスをインスタンス化するオブジェクトのプロパティが変更されると、プロパティの元の値を記録する辞書にエントリが追加されます。

コンテンツ タイプのクラスはすべて、SharePoint Foundation の Item 基本コンテンツ タイプを継承します。したがって、通常は、Item コンテンツ タイプを表すクラスだけが ITrackEntityState および ITrackOriginalValues を実装します。

エンティティ クラスの宣言の生成には、SPMetal ツールを使用することをお勧めします。このツールを使用すると、既定では、Item コンテンツ タイプを表す Item クラスの宣言が生成され、ITrackEntityState および ITrackOriginalValues を実装するようにクラスが構成されます。次の例では、読みやすいように無関係な詳細は省略されています。

[Microsoft.SharePoint.Linq.ContentTypeAttribute(Name="Item", Id="0x01")]
public partial class Item : ITrackEntityState, ITrackOriginalValues, INotifyPropertyChanged, INotifyPropertyChanging
{
    private EntityState _entityState;
    
    private IDictionary<string, object> _originalValues;

    EntityState EntityState { 
        get {
            return this._entityState;
        }
        set {
            this._entityState = value;
        }
    }

    IDictionary<string, object> OriginalValues {
        get {
            return this._originalValues;
        }
        set {
            this._originalValues = value;
        }
    }
    
    public Item() {
        this._entityState = EntityState.Unchanged;
        this.OnCreated();
    }

    // Other member declarations suppressed for readability.
}

最後に、これらのコンテンツ タイプのクラス (または継承元の Item クラス) で INotifyPropertyChanged および INotifyPropertyChanging を実装する必要があります。次のコードは、SPMetal でこれらのインターフェイスが Item クラスにどのように実装されるかを示しています。

[Microsoft.SharePoint.Linq.ContentTypeAttribute(Name="Item", Id="0x01")]
public partial class Item : ITrackEntityState, ITrackOriginalValues, INotifyPropertyChanged, INotifyPropertyChanging
{
    // Material omitted.

    public event PropertyChangedEventHandler PropertyChanged;
    
    public event PropertyChangingEventHandler PropertyChanging;
    
    protected virtual void OnPropertyChanged(string propertyName) {
        if ((EntityState.Unchanged == this._entityState)) {
            this._entityState = EntityState.ToBeUpdated;
        }
        if ((this.PropertyChanged != null)) {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    
    protected virtual void OnPropertyChanging(string propertyName, object value) {
        if ((null == _originalValues)) {
            this._originalValues = new Dictionary<string, object>();
        }
        if ((false == _originalValues.ContainsKey(propertyName))) {
            _originalValues.Add(propertyName, value);
        }
        if ((this.PropertyChanging != null)) {
            this.PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
        }
    }

    // Other member declarations suppressed for readability.
}

この例からわかるように、OnPropertyChanging メソッドの目的は、値が変更されようとしているプロパティの元の値を記録して、PropertyChanging() イベントを発生させることです。一方、OnPropertyChanged メソッドの目的は、リスト アイテム オブジェクトのエンティティの状態を ToBeUpdated に設定した後で PropertyChanged() イベントを発生させることです。この値は、オブジェクトのプロパティの少なくとも 1 つが変更されているために、同時実行の競合が排除されており、SubmitChanges() の次回の呼び出しでコンテンツ データベースの対応するフィールドにその変更が書き込まれることを意味します。目的は、リスト アイテムのフィールドを表すプロパティの set アクセサーでこれら 2 つのメソッドを呼び出すことです。たとえば、次の例は、リスト アイテムの Title フィールドを表す Item クラスのプロパティが SPMetal でどのように作成されるかを示しています。読みやすいように、ここでは生成されるコードを簡略化しています。

[Column(Name="Title", Storage="_title", Required=true, FieldType="Text")]
public string Title {
    get {
        return this._title;
    }
    set {
         if ((this._title != value)) {
            this.OnPropertyChanging("Title", this._title);
            this._title = value;
            this.OnPropertyChanged("Title");
         }
    }
}

DataContext クラス

DataContext クラスは、SharePoint Foundation Web サイトのリストおよびリスト アイテムを表します。したがって、コンテンツ データベースのサブセットと考えることができます。このクラスには、オブジェクト変更追跡システムにとって重要な次の 3 つのメンバーがあります。

  • ObjectTrackingEnabled – コードで SharePoint Foundation のデータを変更する前に、このプロパティを true に設定する必要があります。

  • SubmitChanges() – 同時実行の競合が検出されなかった場合、このメソッドは、前回の呼び出し以降に行われたすべての変更をコンテンツ データベースに書き込みます。競合が検出された場合、このメソッドの動作は、渡されるパラメーターによって変化します。競合が検出されたら直ちに ChangeConflictException をスローして、変更の書き込みを中止するように指定することもできます。また、例外のスローは後回しにして、競合に関係しているフィールドに影響のない変更を引き続きコンテンツ データベースに書き込むように指定することもできます。どちらの場合も、競合を表す 1 つ以上のオブジェクトが ChangeConflicts プロパティに格納されます。

  • ChangeConflicts – このプロパティには、SubmitChanges() の前回の呼び出し時に少なくとも 1 つ検出された同時実行の競合を表すオブジェクトが格納されます。

EntityList<T> クラス

EntityList<TEntity> クラスは、Web サイト上のリストを表します。このクラスには、コンテンツ データベースに書き込むためのさまざまなメソッドがあります。その中でも重要なのが次の 2 つのメソッドです。

  • InsertOnSubmit(TEntity) – このメソッドは、リストに追加される指定のリスト アイテムにマークを付けます。

  • DeleteOnSubmit(TEntity) – このメソッドは、リストから削除される指定のリスト アイテムにマークを付けます。

MemberChangeConflict クラス

MemberChangeConflict クラスは、特定のリスト アイテムの特定のフィールドに関して、同時実行の競合の可能性を表します。このクラスの最も重要なメンバーは次のとおりです。

  • OriginalValue – このプロパティは、値がコンテンツ データベースから最後に取得されたとき、または前回 SubmitChanges() が正常に実行された直後のフィールドの値を表します。

  • DatabaseValue – このプロパティは、コンテンツ データベースのフィールドの現在の値を表します。この値が OriginalValue と異なる場合は、別のユーザーのプロセスによってフィールドが変更されており、MemberChangeConflict は実際の同時実行の競合を表します。

  • CurrentValue – このプロパティは、コードのプロセス内にあるフィールドの最新の値を表します ("クライアント値" とも呼ばれます。ただし、ここでの "クライアント" はフロントエンド Web サーバーを意味します)。コードでフィールドの値を変更した場合、CurrentValueOriginalValue は異なります。

  • Resolve() – このメソッドは、SubmitChanges() の次回の呼び出しでフィールドにどの値を適用するかを選択することにより、競合を解決します。

MemberChangeConflict のオブジェクトは、ObjectChangeConflict オブジェクトの MemberConflicts プロパティに格納されます。

重要重要

リスト アイテムのフィールドが同時実行の競合を示している場合、つまり DatabaseValueOriginalValue と異なる場合、MemberChangeConflict はそのフィールドに対してだけでなく、DatabaseValueCurrentValue と異なるすべてのフィールドに対してもインスタンス化されます。これは、リスト オブジェクトのフィールドに対する変更はすべてか無かのベースで行う必要がある場合が多いためです。たとえば、郵便番号フィールドで同時実行の競合が発生し、別のユーザーの変更を残す形で競合を解決する場合は、都市名など、住所の他のフィールドも変更できなくなります。したがって、クライアント値とデータベース値の不一致をすべて表して解決する必要があります。

ObjectChangeConflict クラス

ObjectChangeConflict クラスは、少なくとも 1 つのフィールドで同時実行の競合が発生しているリスト アイテムを表します。このクラスの最も重要な 2 つのメンバーは次のとおりです。

  • MemberConflicts – このプロパティは MemberChangeConflict のオブジェクトのコレクションを表します。オブジェクトの少なくとも 1 つが同時実行の競合を表します。他のフィールドがクライアント値とデータベース値の不一致を示している場合は、それらを表す MemberChangeConflict のオブジェクトもあります。

  • Resolve() – このメソッドは、MemberConflicts プロパティの各メンバーの MemberChangeConflict.Resolve() メソッドを明示的に呼び出すことなく、すべての不一致を解決する手段を提供します。

ChangeConflictCollection クラス

ChangeConflictCollection オブジェクトは、同時実行の競合が少なくとも 1 つ検出された SubmitChanges() の呼び出しによって生成された ObjectChangeConflict のすべてのオブジェクトのコレクションです。最も重要なメンバーは ResolveAll() メソッドで、呼び出し元コードから 1 回メソッドを呼び出すだけで子のすべての競合を解決できます。

ChangeConflictCollection のオブジェクトは、DataContext.ChangeConflicts プロパティに格納されます。

関連項目

その他の技術情報

How to: Write to the Content Databases Using LINQ to SharePoint