LINQ to SharePoint を使用してコンテンツ データベースに書き込む

最終更新日: 2015年3月9日

適用対象: SharePoint Foundation 2010

この記事の内容
手順 1 および 2: Web サイトおよびリストへの参照を取得する
手順 3: オブジェクトの変更履歴が有効になっていることを確認する
手順 4: リスト アイテムを追加、削除、リサイクル、または更新するための Basic コードを追加する
手順 5: 同時実行の競合を管理するためのインフラストラクチャを追加する

このトピックでは、LINQ to SharePoint プロバイダーに対してコーディングを行い、Microsoft SharePoint Foundation リストのリスト アイテムを追加および削除したり、リスト アイテムの特定のフィールドの値を変更したりする方法について説明します。

手順 1 および 2: Web サイトおよびリストへの参照を取得する

データを変更するためにコード内で Web サイトおよびリストへの参照を取得する方法については、トピック「[方法] LINQ to SharePoint を使用してクエリを実行する」の手順 1. と 2. を参照してください。

手順 3: オブジェクトの変更履歴が有効になっていることを確認する

LINQ to SharePoint を使用してデータベースを変更できるようにするには、ObjectTrackingEnabled プロパティは、必ずデフォルトの true でなければなりません。コードによってこのプロパティが false に設定されている場合、この値が設定された DataContext オブジェクトにクエリを実行すると、プロパティは true にリセットできません。したがって、コードによって DataContext オブジェクトに対してクエリを実行し、コンテンツ データベースに変更を加える場合は、2 つのオプションがあります。

  • コードによって ObjectTrackingEnabled プロパティが false に設定されないようにします。

  • コードで同じ Web サイトの DataContext オブジェクトを新しく作成し、クエリの後、ただしデータベースへのコードの書き込みの前にそれを配置します。新しい DataContext オブジェクトの ObjectTrackingEnabled プロパティはデフォルトの true のままにし、新しいオブジェクトを使用して、コンテンツ データベースへの書き込みを行います。

手順 4: リスト アイテムを追加、削除、リサイクル、または更新するための Basic コードを追加する

コンテンツ データベースに書き込むための Basic コードはシンプルです。最初に Web サイトおよびリストへの参照を作成し、最後に SubmitChanges() を呼び出して終了します。途中のコードでは、EntitySet<TEntity> クラスの *OnSubmit メソッドのいずれかを使用して変更を加えるか、通常のプロパティ設定構文を使用してリスト フィールドに対して書き込みを行います。以下のすべての例で、teamSite は Web サイトを表す DataContext オブジェクトです。また、TeamMembers は [チーム メンバー] リストを表す EntitySet<TEntity> オブジェクトです。

重要重要

追加、削除、リサイクル、または更新するリスト アイテムを表すオブジェクトには EntityState プロパティ、Id プロパティ、および Version プロパティが必要です。コンテンツ タイプを表すクラスが SPMetal によって生成された場合、これらのプロパティは存在します。EntityState プロパティには、さまざまな *OnSubmit メソッドによって値が割り当てられます。また、Id プロパティおよび Version プロパティは、SharePoint Foundation ランタイムによって設定されます。この 3 つのプロパティは、コードによって書き込まれないようにする必要があります。

リスト アイテムを追加する

アイテムをリストに追加するには、リストのコンテンツ タイプのオブジェクトを作成し、それを InsertOnSubmit(TEntity) メソッドに渡します。

重要重要

コンテンツ タイプの必須フィールドを表すリスト アイテムのプロパティの値は、リストにアイテムが挿入される前に設定されていなければなりません (このプロパティは、必須プロパティが存在し true に設定されている ColumnAttribute 装飾で宣言されています)。たとえば、コンテンツ タイプはすべて SharePoint Foundation の基本の Item コンテンツ タイプから継承されます。このコンテンツ タイプは必須の Title フィールドです。こうしたプロパティには、InsertOnSubmit(TEntity) の呼び出しと SubmitChanges() の呼び出しの間に値を割り当てることができますが、必須プロパティはできるだけ早く初期化することをお勧めします。すべての必須プロパティを初期化するクラス コンストラクターがない場合は、次の例で示すように、オブジェクト イニシャライザーを使用できます。

次の例は、リストにアイテムを追加して、データベースへの変更を保存する方法を示しています。

// Create the new list item.
TeamMember bob = new TeamMember() { Title="Bob Smith" };

// Set the item to be inserted.
teamSite.TeamMembers.InsertOnSubmit(bob);

// Write changes to the content database.
teamSite.SubmitChanges();

複数のアイテムを挿入する際に使用できる InsertAllOnSubmit(IEnumerable<TEntity>) メソッドもあります。

リスト アイテムを削除およびリサイクルする

次の例は、DeleteOnSubmit(TEntity) メソッドを使用してリストからアイテムを削除する方法を示しています。

// Set the item to be deleted.
foreach (TeamMember teamMember in teamSite.TeamMembers)
{
    if (teamMember.Title = "Bob Smith")
    {
        teamSite.TeamMembers.DeleteOnSubmit(teamMember);
    }
}

// Write changes to the content database.
teamSite.SubmitChanges();

アイテムを完全に削除するのではなく、ユーザーのごみ箱に移動するには、RecycleOnSubmit(TEntity) メソッドを使用します。DeleteAllOnSubmit(IEnumerable<TEntity>) メソッドおよび RecycleAllOnSubmit(IEnumerable<TEntity>) メソッドを使用して、複数のアイテムを同時に削除することもできます。

リスト アイテムのフィールドを変更する

リスト アイテムのフィールドの値を変更するには、次の例で示すように、フィールドを表すプロパティにただ書き込むだけです。

// Set the property to a new value.
foreach (TeamMember teamMember in teamSite.TeamMembers)
{
    teamMember.TopTask = "Fiscal Planning";
}

// Write changes to the content database.
teamSite.SubmitChanges();

1 回の SubmitChanges 呼び出しによる複数の変更

SubmitChanges() メソッドを 1 回呼び出すことによってコードで変更できる数に制限はありません。最適なパフォーマンスを確保するため、コードで行う呼び出しの数はできるだけ少なく抑えるようにします。次のコードは、SubmitChanges() を 1 回呼び出すことでコンテンツ データベースに書き込まれるさまざまな種類の変更の例を示しています。

// ‘sally’ is a TeamMember object.
teamSite.TeamMembers.RecycleOnSubmit(sally);

// ‘leftCompany’ is an IList of TeamMember objects
teamSite.TeamMembers.DeleteAllOnSubmit(leftCompany);

foreach (TeamMember teamMember in teamSite.TeamMembers)
{
    teamMember.TopTask = "Fiscal Planning";
}

// Write changes to the content database.
teamSite.SubmitChanges();

送信する変更は必ずしもすべてが同じリストにある必要はありません。Web サイトの任意のリストおよびすべてのリストに対する変更を SubmitChanges() の 1 回の呼び出しで送信できます。

手順 5: 同時実行の競合を管理するためのインフラストラクチャを追加する

フィールドの値を変更した場合、SubmitChanges() メソッドの呼び出しによって変更がコンテンツ データベースにコミットされる前に、変更の影響を受けるリスト アイテムを現在のユーザー プロセスがデータベースから取得した後、他のユーザーによってそのリスト アイテムが変更されていないかどうかが、オブジェクト変更履歴システムによって確認されます (このシステムの詳細については、「オブジェクト変更追跡とオプティミスティック同時実行制御」を参照してください)。同時実行の競合がある場合は、SubmitChanges() によって ChangeConflictException がスローされます。また、不一致に関する情報を示す MemberChangeConflict オブジェクトが生成されます。フィールドの現在のクライアント値とデータベースのフィールドの値の間に他の不一致がある場合は、それも MemberChangeConflict オブジェクトによって表されます。指定したリスト アイテムの不一致はすべて、ObjectChangeConflict オブジェクトの MemberConflicts プロパティに追加されます。SubmitChanges() のどのオーバーロードが呼び出されたか、およびどのパラメーターが渡されたかによって、複数の ObjectChangeConflict オブジェクトが存在する場合があります。このオブジェクトはすべて、DataContext オブジェクトの ChangeConflicts プロパティに追加されます。

コードは SubmitChanges() を再度呼び出す前に、例外をキャッチして、すべての不一致を解決する必要があります。状況によっては、発生した不一致ごとに解決方法を決定するようにユーザーに求めるのが、最適な対応策であることがあります。ユーザーに提供する UI には、ObjectChangeConflict オブジェクトの ChangeConflicts プロパティのデータ、およびその MemberChangeConflict 子オブジェクトのデータ、特に OriginalValue プロパティ、DatabaseValue プロパティ、および CurrentValue プロパティのデータを設定できます。

ヒントヒント

UI では、実際の同時実行の競合を表す不一致、つまり OriginalValue プロパティと DatabaseValue プロパティ間の不一致と、同時実行の競合が存在しない場合に自動的にコンテンツ データベースに書き込まれる変更を表す不一致、つまり、OriginalValue = DatabaseValue の場合の DatabaseValue プロパティと CurrentValue プロパティ間の不一致を明確に区別することを検討してください。

次のメソッドを組み合わせて呼び出すことでユーザーの選択内容を実装します。

また、アプリケーションの目的やコンテンツ データベースに対する変更の種類に基づいて、不一致の最善の解決策をコーディングするタイミングを判断できることがあります。このような場合は、ユーザーにメッセージを表示せずに、前述のメソッドの組み合わせを呼び出すことが最適である場合があります。解決ロジックを構築するにあたり、主に 4 つの点について考慮する必要があります。

  • どのバージョンのフィールド値をコンテンツ データベースに保持する必要があるか。フィールド値のバージョンには元の値、データベースの現在の値、アプリケーションのプロセスの値 (クライアント値) などの値があります。ガイダンスについては、「RefreshMode」および前述したリストのメソッドに関するリファレンス トピックを参照してください。また、次の表では、LINQ to SQL プロバイダーに関する記事を紹介していますが、この記事で説明されているロジックは LINQ to SharePoint にも適用されます。

    方法: データベース値とマージすることで同時実行の競合を解決する (LINQ to SQL)

    方法: データベース値を維持することで同時実行の競合を解決する (LINQ to SQL)

    方法: データベース値を上書きすることで同時実行の競合を解決する (LINQ to SQL)

  • 現在のユーザーが、別のユーザーによってリストから完全に削除された変更をリスト アイテムに送信した場合はどうするか。オプションの詳細については、「Resolve(RefreshMode, Boolean)」および「ResolveAll(RefreshMode, Boolean)」を参照してください。

  • 前述の 2 つのシナリオについて、どのくらいのレベルで判断を適用するか。たとえば、次のルールを、すべてのリストのすべてのリスト アイテムに対する不一致に適用するとします。

    • 他のユーザーから削除されたリスト アイテムを無視する。

    • 自分のアプリケーション プロセスによって行われたすべての変更のほか、他のユーザー プロセスの変更も保持し、別のユーザーとの間に競合が発生した場合は自分の変更を優先させる。

    このロジックは、ChangeConflictCollection.ResolveAll() を呼び出すだけで適用できます。

    ただし、一部のアイテム リストのアイテムですべての変更を保持しながら、他の特定のリストのアイテムで自分のプロセスの変更を取り消す場合は、コードによって ChangeConflicts プロパティを反復処理する必要があります。そして、ObjectChangeConflict.Resolve() の別のオーバーロードを呼び出して、別の ObjectChangeConflict アイテムに対する別のパラメーターを渡します。

    一部のコンテンツ タイプでは、さまざまなフィールドに対してさまざまなルールを適用しなければならないことがあります。このような状況では、コードによって ObjectChangeConflict オブジェクトの MemberConflicts プロパティのメンバーを反復処理して、MemberChangeConflict.Resolve() のさまざまなオーバーロードを呼び出して、さまざまなフィールドに対するさまざまなパラメーターを渡します。

  • いつ ChangeConflictException 例外をスローして、変更が行われないようにするか。これは、解決ロジックの一部ですが、自分の判断は SubmitChanges() を呼び出して実装します。これには 2 つのオプションがあります。一方のオプションを使用すると、同時実行の競合が検出されたときに直ちに例外をスローできます。もう一方のオプションを使用すると、保留中のすべての変更を引き続き書き込むことができます。後者の場合は、1 つ以上の同時実行の競合が検出された場合に例外がスローされます。また、競合があるフィールドに対する変更は取り消されます。2 番目のオプションの利点は、送信された変更セット全体にある同時実行の競合の明細表示 (および不一致) すべてが、MemberConflicts プロパティに含まれることです。これにより、解決ロジックはセット全体を処理できます。どのオプションを使用するかは、呼び出す SubmitChanges() のオーバーロードと、そのオーバーロードにパスするパラメーターによって決まります。

次のコードは、すべての不一致を最も簡単に解決する方法を示しています。

foreach (TeamMember teamMember in teamSite.TeamMembers)
{
    teamMember.TopTask = "Fiscal Planning";
}

try 
{
    teamSite.SubmitChanges();
}
catch (ChangeConflictException e) 
{
    teamSite.ChangeConflicts.ResolveAll();
    teamSite.SubmitChanges();
}

SubmitChanges() の最初の呼び出しはパラメーターなしオーバーロードです。最初の同時実行の競合が検出されるとすぐに、このオーバーロードによって例外がスローされます。したがって、ChangeConflictCollection.ResolveAll() の呼び出しでは、それまでに記録されたすべての不一致しか解決されません。送信された変更間に同時実行の競合が他にある場合は、SubmitChanges() の 2 回目の呼び出しで例外がスローされます。

次のコードは、不一致を解決するためのより複雑な例を示しています。

foreach (TeamMember teamMember in teamSite.TeamMembers)
{
    teamMember.TopTask = "Fiscal Planning";
}

try 
{
    teamSite.SubmitChanges(ConflictMode.ContinueOnConflict);
}
catch (ChangeConflictException e) 
{
    foreach (ObjectChangeConflict changedListItem in teamSite.ChangeConflicts)
    {
        // If another user has changed properties of a non-manager,
        // leave that other user’s changes, except for the TopTask field.
        if (((TeamMember)changedListItem.Object).IsManager = false)
        {        
             foreach (MemberChangeConflict changedField in changedListItem.MemberConflicts)
            {
                if (changedField.Member.Name == "TopTask")
                {
                    changedField.Resolve(RefreshMode.KeepCurrentValues);
                }
                else
                {
                    changedField.Resolve(RefreshMode.OverwriteCurrentValues);
                }
            }
        }
        // But if another user has changed properties of a manager, let this
        // process’s changes override the other user’s changes.
        else
        {
            changedListItem.Resolve(RefreshMode.KeepCurrentValues);
        }    
    }

    teamSite.SubmitChanges();
} // end catch