次の方法で共有


ファーム ソリューションのリスト定義から作成された SharePoint リストを置換する

リスト定義を使用してファーム ソリューションでリストを作成した場合、クライアント オブジェクト モデル (CSOM) を使用してそれらのリストから同様の機能を提供する新しいソリューションに変換する方法について理解してください。

重要

ファーム ソリューションは、SharePoint Online に移行できません。 この記事で取り上げられている技法とコードを適用すると、ファーム ソリューションと同様の機能を提供する新しいソリューションを構築して、SharePoint Online に展開できます。 インプレース変換方式を使用している場合は、新しいソリューションを SharePoint Online に展開することが必要になることもあります。 スウィング方式またはコンテンツ移行方式を使用している場合は、サード パーティ製のツールでリストを作成してください。 詳細については、「新しい SharePoint アドインを展開するための変換方法」を参照してください。

完全に機能するソリューションを実現するには、この記事のコードに、追加のコードが必要になります。 たとえば、この記事では、Office 365 に対する認証を実行する方法や、必要な例外処理を実装する方法などについては取り上げていません。 追加のコード サンプルについては、「Office 365 Developer Patterns and Practices プロジェクト」を参照してください。

注:

この記事で提供されるコードは、明示または黙示のいかなる種類の保証なしに現状のまま提供されるものであり、特定目的への適合性、商品性、権利侵害の不存在についての暗黙的な保証は一切ありません。

ヒント

この記事で説明されている技法は、一度に 2、3 個のリストのみを更新する場合に使用してください。 また、更新対象のリストを検索するときには、リストの種類によるフィルターを適用しないようにしてください。

リスト定義から作成されたリストを置換するには、CSOM を使用して以下の作業を行います。

  1. 特定の基本テンプレートを使用して作成されたリストを検出します。

  2. すぐに使用可能なリスト定義を使用して新しいリストを作成します。

  3. リスト設定を構成します。

  4. 元のリストで設定されていたコンテンツ タイプに基づいて新しいリストでコンテンツ タイプを設定します。

  5. ビューを追加または削除します (省略可能)。

  6. 元のリストのコンテンツを新しいリストに移行します。

はじめに

既存のファーム ソリューションについて確認し、この記事で取り上げられている技法について理解し、その後、既存のファーム ソリューションにそうした技法を適用する方法を計画するのが理想的です。 ファーム ソリューションに精通していない場合、または作業対象の既存のファーム ソリューションがない場合には、以下の事柄が役立つ可能性があります。

  1. リスト定義を使用した宣言によるリスト作成方法の概要を把握するには、「カスタム テンプレートに基づいて作成されたリストの置換」を参照してください。

    注:

    Contoso.Intranet の場合、SP\ListTemplate\LTContosoLibrary の elements.xml ファイル内でのカスタム リスト テンプレートの ID は 10003 です。 これは、リストの作成に使ったテンプレートを識別するのに使います。 また、リストに定義されている構成設定とビューを確認します。

  2. ファーム ソリューションについて理解します。 詳細については、「SharePoint でのファーム ソリューションの作成」を参照してください。

特定の基本テンプレートを使用して作成されたリストを検出します。

次のコードは、特定の基本テンプレートを使用して作成されたリストを検出する方法を示しています。 この方式では、以下の操作が実行されます。

  1. ClientContext を使用し、Web.Lists を使って現在の Web 内にあるすべてのリストを取得します。 リストのコレクションを返すためにラムダ式で Include ステートメントが使用されます。 返されるすべてのリストでは、BaseTemplateBaseType、および Title プロパティの値が指定されている必要があります。

  2. 返されたリストのコレクションの各リストについて、List.BaseTemplate の値が 10003 と等しい場合は、そのリストを置換対象のリストのコレクション (listsToReplace) に追加します。 10003 は、Contoso.Intranet サンプルで確認したカスタム リスト テンプレートの識別子であることに注意してください。

     static void Main(string[] args)
     {
         using (var clientContext = new ClientContext("http://w15-sp/sites/ftclab"))
         {
             Web web = clientContext.Web;
             ListCollection listCollection = web.Lists;
             clientContext.Load(listCollection,
                                 l => l.Include(list => list.BaseTemplate,
                                                 list => list.BaseType,
                                                 list => list.Title));
             clientContext.ExecuteQuery();
             var listsToReplace = new List<List>();
             foreach (List list in listCollection)
             {
                 // 10003 is the custom list template ID of the list template you're replacing.
                 if (list.BaseTemplate == 10003)
                 {
                     listsToReplace.Add(list);
                 }
             }
             foreach (List list in listsToReplace)
             {
                 ReplaceList(clientContext, listCollection, list);
             }
         }
     }
    

    重要

    上記のコードでは、最初に ListCollection を反復処理して変更が必要なリストを選択してから、リストの変更を開始する ReplaceList を呼び出しています。 このパターンが必要になる理由は、コレクションの反復処理中にコレクションのコンテンツに変更があると、例外がスローされるためです。

  3. 置換するリストを特定した後、リストの置換操作を実行する順序は、ReplaceList に示されています。

     private static void ReplaceList(ClientContext clientContext, ListCollection listCollection, List listToBeReplaced)
     {
         var newList = CreateReplacementList(clientContext, listCollection, listToBeReplaced);
    
         SetListSettings(clientContext, listToBeReplaced, newList);
    
         SetContentTypes(clientContext, listToBeReplaced, newList);
    
         AddViews(clientContext, listToBeReplaced, newList);
    
         RemoveViews(clientContext, listToBeReplaced, newList);
    
         MigrateContent(clientContext, listToBeReplaced, newList);
     }
    

新しいリストを作成する

新しいリストを作成するために、CreateReplacementListListCreationInformation を使用します。

新しいリストのタイトルは、既存のリストのタイトルの末尾に Add-in が付加された形に設定されます。 リストのテンプレートの種類をドキュメント ライブラリに設定するには、ListTemplateType 列挙型を使用します。 別のテンプレートの種類に基づいてリストを作成する場合は、該当する正しいテンプレートの種類を使用してください。 たとえば、予定表リストを作成する場合、ListTemplateType.DocumentLibrary ではなく ListTemplateType.Events を使用します。

private static List CreateReplacementList(ClientContext clientContext, ListCollection lists,List listToBeReplaced)
{
    var creationInformation = new ListCreationInformation
    {
        Title = listToBeReplaced.Title + "add-in",
        TemplateType = (int) ListTemplateType.DocumentLibrary,
    };
    List newList = lists.Add(creationInformation);
    clientContext.ExecuteQuery();
    return newList;
}

リスト設定を構成する

SetListSettings は、以下のようにして元のリスト設定を新しいリストに適用します。

  1. 元のリストの各種リスト設定を取得します。

  2. 元のリストのリスト設定を新しいリストに適用します。

private static void SetListSettings(ClientContext clientContext, List listToBeReplaced, List newList)
{
    clientContext.Load(listToBeReplaced, 
                        l => l.EnableVersioning, 
                        l => l.EnableModeration, 
                        l => l.EnableMinorVersions,
                        l => l.DraftVersionVisibility );
    clientContext.ExecuteQuery();
    newList.EnableVersioning = listToBeReplaced.EnableVersioning;
    newList.EnableModeration = listToBeReplaced.EnableModeration;
    newList.EnableMinorVersions= listToBeReplaced.EnableMinorVersions;
    newList.DraftVersionVisibility = listToBeReplaced.DraftVersionVisibility;
    newList.Update();
    clientContext.ExecuteQuery();
}

注:

それぞれの要件に応じて、元のリストのリスト設定は異なる可能性があります。 自分のリスト設定を確認して、元のリスト設定が確実に新しいリストに適用されるように SetListSettings を変更してください。

新しいリストにコンテンツ タイプを設定する

SetContentTypes は、次のようにして新しいリストにコンテンツ タイプを設定します。

  1. 元のリストからコンテンツ タイプ情報を取得します。

  2. 新しいリストからコンテンツ タイプ情報を取得します。

  3. 元のリストでコンテンツ タイプが有効にされているかどうかを判別します。 元のリストでコンテンツ タイプが有効になっていない場合、SetContentTypes は終了します。 元のリストでコンテンツ タイプが有効になっている場合は、newList.ContentTypesEnabled = true を使用して SetContentTypes で新しいリストのコンテンツ タイプを有効にします。

  4. 元のリストのコンテンツ タイプごとに、新しいリストでコンテンツ タイプを検索し、 newList.ContentTypes.Any(ct => ct) を使用して、コンテンツ タイプの名前に基づいて一致するコンテンツ タイプを検索します。Name == contentType.Name)。 そのコンテンツ タイプが新しいリストにない場合、それがリストに追加されます。

  5. AddExistingContentType によってコンテンツ タイプが変更されている可能性があるため、newList をもう一度読み込みます。

  6. newList のコンテンツ タイプごとに、listToBeReplaced.ContentTypes.Any(ct => ct) を使用して、ContentType.Name に基づいてコンテンツ タイプが元のリストのコンテンツ タイプと一致するかどうかを判断します。Name == contentType.Name)。 一致が見つからない場合、新しいリストから削除する contentTypesToDelete に、そのコンテンツ タイプが追加されます。

  7. ContentType.DeleteObject を呼び出して、コンテンツ タイプを削除します。

注:

インプレース変換方式を使用している状況で、機能フレームワークを使ってコンテンツ タイプが宣言的に展開された場合は、次に示す操作が必要になります。

  1. 新しいコンテンツ タイプを作成します。
  2. 元のリストのコンテンツを新しいリストに MigrateContent を使用して移行するときに、リスト項目にコンテンツ タイプを設定します。
private static void SetContentTypes(ClientContext clientContext, List listToBeReplaced, List newList)
{
    clientContext.Load(listToBeReplaced,
                        l => l.ContentTypesEnabled,
                        l => l.ContentTypes);
    clientContext.Load(newList,
                        l => l.ContentTypesEnabled,
                        l => l.ContentTypes);
    clientContext.ExecuteQuery();

    // If the original list doesn't use content types, do not proceed any further.
    if (!listToBeReplaced.ContentTypesEnabled) return;

    newList.ContentTypesEnabled = true;
    newList.Update();
    clientContext.ExecuteQuery();
    foreach (var contentType in listToBeReplaced.ContentTypes)
    {
        if (!newList.ContentTypes.Any(ct => ct.Name == contentType.Name))
        {
            // Current content type needs to be added to the new list. Note that the content type is added to the list, not the site.           
            newList.ContentTypes.AddExistingContentType(contentType.Parent);
            newList.Update();
            clientContext.ExecuteQuery();
        }
    }
    // Reload the content types on newList because they might have changed when AddExistingContentType was called. 
    clientContext.Load(newList, l => l.ContentTypes);
    clientContext.ExecuteQuery();
    // Remove any content types that are not needed.
    var contentTypesToDelete = new List<ContentType>();
    foreach (var contentType in newList.ContentTypes)
    {
        if (!listToBeReplaced.ContentTypes.Any(ct => ct.Name == contentType.Name))
        {
            // Current content type needs to be removed from new list.
            contentTypesToDelete.Add(contentType);
        }
    }
    foreach (var contentType in contentTypesToDelete)
    {
        contentType.DeleteObject();
    }
    newList.Update();
    clientContext.ExecuteQuery();
}

注:

この時点で、新しいリストは元のリストからのコンテンツを受け入れることができます。 また、オプションとしてビューを追加または削除できます。

ビューを追加または削除する (省略可能)

ユーザーは、リストに定義されるビューをビジネス ニーズに応じて追加または削除することができます。 したがって、新しいリストでビューを追加または削除する必要が生じることがあります。

新しいリストにビューを追加する

AddViews は、次のように元のリストのビューを新しいリストに追加します。

  1. List.Views を使用して、元のリストのすべてのビューを取得します。

  2. ラムダ式を使用して、さまざまなビュー プロパティを views コレクションに読み込みます。

  3. 元のリストのビューごとに、ViewCreationInformation を使用してビューを作成します。

    元のビューのビュー プロパティに基づいて、さまざまなプロパティが新しいビューで設定されます。 ビューの ViewType を判別するために、GetViewType ヘルパー メソッドが呼び出されます。 その後、新しいビューが viewsToCreate というビュー リストに追加されます。

  4. リストの Views コレクションに対して Add メソッドを使用し、新しいビューを List.Views コレクションに追加します。

     private static void AddViews(ClientContext clientContext, List listToBeReplaced, List newList)
     {
         ViewCollection views = listToBeReplaced.Views;
         clientContext.Load(views,
                             v => v.Include(view => view.Paged,
                                 view => view.PersonalView,
                                 view => view.ViewQuery,
                                 view => view.Title,
                                 view => view.RowLimit,
                                 view => view.DefaultView,
                                 view => view.ViewFields,
                                 view => view.ViewType));
         clientContext.ExecuteQuery();
    
         // Build a list of views which exist on the original list only.
         var viewsToCreate = new List<ViewCreationInformation>();
         foreach (View view in listToBeReplaced.Views)
         {
         var createInfo = new ViewCreationInformation
         {
             Paged = view.Paged,
             PersonalView = view.PersonalView,
             Query = view.ViewQuery,
             Title = view.Title,
             RowLimit = view.RowLimit,
             SetAsDefaultView = view.DefaultView,
             ViewFields = view.ViewFields.ToArray(),
             ViewTypeKind = GetViewType(view.ViewType),
         };
         viewsToCreate.Add(createInfo);
         }
    
         foreach (ViewCreationInformation newView in viewsToCreate)
         {
             newList.Views.Add(newView);
         }
         newList.Update();
     }
    
     private static ViewType GetViewType(string viewType)
     {
         switch (viewType)
         {
             case "HTML":
                 return ViewType.Html;
             case "GRID":
                 return ViewType.Grid;
             case "CALENDAR":
                 return ViewType.Calendar;
             case "RECURRENCE":
                 return ViewType.Recurrence;
             case "CHART":
                 return ViewType.Chart;
             case "GANTT":
                 return ViewType.Gantt;
             default:
                 return ViewType.None;
         }
     }
    

新しいリストからビューを削除する

次のコードでは、RemoveViews が次のようにして新しいリストからビューを削除します。

  1. List.Views プロパティを使用して、新しいリストのリスト ビューを取得します。

  2. 新しいリストの各ビューについて、 !listToBeReplaced.Views.Any(v => v.Title == ビューを使用して、ビューのタイトルに一致することによって、そのビューが元のリストに存在しないかどうかを判断します。タイトル。 元のリスト内のビューと一致するビューが新しいリストにない場合、そのビューを viewsToRemove に追加します。

  3. View.DeleteObject を使用して、viewsToRemove に含まれているすべてのビューを削除します。

     private static void RemoveViews(ClientContext clientContext, List listToBeReplaced, List newList)
     {
         // Get the list of views on the new list.
         clientContext.Load(newList, l => l.Views);
         clientContext.ExecuteQuery();
    
         var viewsToRemove = new List<View>();
         foreach (View view in newList.Views)
         {
             if (!listToBeReplaced.Views.Any(v => v.Title == view.Title))
             {
                 // If there is no matching view in the source list, add the view to the list of views to be deleted.
                 viewsToRemove.Add(view);
             }
         }
         foreach (View view in viewsToRemove)
         {
             view.DeleteObject();
         }
         newList.Update();
         clientContext.ExecuteQuery();
     }
    

元のリストのコンテンツを新しいリストに移行する

MigrateContent は、次のようにして元のリストのコンテンツを新しいリストに移行します。

  1. List.RootFolder を使用して、ファイルのコピー先または新しいリストのルート フォルダーを取得します。 保存先リスト フォルダーのサーバー相対 URL は、 Folder.ServerRelativeUrl を使用して取得されます。

  2. List.RootFolder を使用して、ファイルのソース、つまり元のリストのルート フォルダーを取得します。 リスト フォルダーのサーバー相対 URL とソースのルート フォルダー内のすべてのファイルが、clientContext オブジェクトを使用して読み込まれます。

  3. ソース内のファイルごとに、そのファイルの新しい URL を保管する newUrl を作成します。 newUrl は、ソースのルート フォルダーを宛先のルート フォルダーに置き換えることによって作成されます。

  4. File.CopyTo を使用して、ファイルを宛先ルート フォルダーの URL にコピーします。 または、 File.MoveTo メソッドを使用して宛先 URL にファイルを移動することもできます。

注:

次に示すコードは、すべてのリスト項目を返します。 運用環境では、ループを実装して、少数のリスト項目を移行する複数の反復処理を使用することで、このコードを最適化することを検討してください。

private static void MigrateContent(ClientContext clientContext, List listToBeReplaced, List newList)
{
    ListItemCollection items = listToBeReplaced.GetItems(CamlQuery.CreateAllItemsQuery());
    Folder destination = newList.RootFolder;
    Folder source = listToBeReplaced.RootFolder;
    clientContext.Load(destination,
                        d => d.ServerRelativeUrl);
    clientContext.Load(source,
                        s => s.Files,
                        s => s.ServerRelativeUrl);
    clientContext.Load(items,
                        i => i.IncludeWithDefaultProperties(item => item.File));
    clientContext.ExecuteQuery();


    foreach (File file in source.Files)
    {
        string newUrl = file.ServerRelativeUrl.Replace(source.ServerRelativeUrl, destination.ServerRelativeUrl);
          file.CopyTo(newUrl, true);
          //file.MoveTo(newUrl, MoveOperations.Overwrite);
    }
    clientContext.ExecuteQuery();
}

注:

上記のコードは、リストのルート フォルダーに格納されているファイルを移行する方法を示しています。 リストにサブフォルダーが含まれている場合は、サブフォルダーとその内容を移行するためのコードを追加する必要があります。 リストでワークフローを使用する場合は、ワークフローを新しいリストに関連付けるための追加のコードが必要になります。

関連項目