在场解决方案中替换根据列表定义创建的 SharePoint 列表

如果使用列表定义在场解决方案中创建列表,了解如何使用客户端对象模型 (CSOM) 将其转换为提供类似功能的新解决方案。

重要

不能将场解决方案迁移到 SharePoint Online。 通过应用本文中介绍的方法和代码,你可以使用服务器场解决方案提供的类似功能构建一个新的解决方案,然后可以将该解决方案部署到 SharePoint Online。 如果使用就地转换方法,你可能需要将新的解决方案部署到 SharePoint Online。 如果你使用 Swing 或内容迁移方法,第三方工具可能为你创建列表。 有关详细信息,请参阅用于部署新 SharePoint 外接程序的转换方法

本文中的代码需要额外的代码,以提供正常运行的解决方案。 例如,本文不讨论如何对 Office 365 进行身份验证、如何实现必需的异常处理,等等。 有关其他代码示例,请参阅 Office 365 开发人员模式和做法项目。

注意

本文中的代码按原样提供,不提供任何明示或暗示的担保,包括对特定用途适用性、适销性或不侵权的默示担保。

提示

使用本文所述技术一次仅更新几个列表。 此外,查找要更新的列表时,不应按列表类型筛选列表。

要使用 CSOM 替换根据列表定义创建的列表,请执行以下操作:

  1. 查找使用特定的基本模板创建的列表。

  2. 使用现成的列表定义创建新列表。

  3. 配置列表设置。

  4. 根据原始列表上设置的内容类型设置新列表上的内容类型。

  5. 添加或删除视图(可选)。

  6. 将内容从原始列表迁移到新列表。

准备工作

理想情况下,您应审核现有的服务器场解决方案,了解本文中所述的技术,然后计划如何将这些技术应用到现有的服务器场解决方案。 如果您不熟悉服务器场解决方案或者没有可使用的现有服务器场解决方案,执行以下操作可能会很有帮助:

  1. 审阅替换从自定义模板创建的列表以快速了解如何使用列表定义以声明方式创建列表。

    注意

    在 Contoso.Intranet 中,SP\ListTemplate\LTContosoLibrary 的 elements.xml 文件中自定义列表模板的 ID 为 10003。 我们将使用它识别创建列表所采用的模板。 另请查看列表上定义的配置设置和视图。

  2. 了解场解决方案。 有关详细信息,请参阅在 SharePoint 中生成场解决方案

查找使用特定的基本模板创建的列表

在以下代码中,此方法演示如何查找使用特定基本模板创建的列表。 此方法:

  1. 使用 ClientContext,通过 Web.Lists 获取当前 Web 中的所有列表。 在 lambda 表达式中,Include 语句用于返回列表集合。 返回的列表必须都具有针对 BaseTemplateBaseTypeTitle 指定的属性值。

  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);
     }
    

创建新的列表

若要创建新列表,CreateReplacementList 需使用 ListCreationInformation

新列表标题设置为现有列表标题,并附加有外接程序。 ListTemplateType 枚举用于将清单的模板类型设置为文档库。 如果要创建基于不同模板类型的列表,请确保使用正确的模板类型。 例如,如果要创建日历列表,请使用 ListTemplateType.Events 而不是 ListTemplateType.DocumentLibrary

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 将退出。 如果原始列表启用内容类型,SetContentTypes 将通过使用 newList.ContentTypesEnabled = true 在新列表中启用内容类型。

  4. 对于原始列表中的每种内容类型,使用 newList.ContentTypes.Any (ct => ct 搜索新列表中的内容类型,以根据内容类型的名称查找匹配的内容类型。Name == contentType.Name) 。 如果内容类型不在新列表中,将其添加到列表。

  5. 再次加载 newList,因为 AddExistingContentType 可能已更改内容类型。

  6. 对于 newList 中的每个内容类型,通过使用 listToBeReplaced.ContentTypes.Any (ct = ct,>根据 ContentType.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. 使用 lambda 表达式在 views 集合上加载各种视图属性。

  3. 对于原始列表中的每个视图,请使用 ViewCreationInformation 创建视图。

    基于原始视图的视图属性在新视图上设置各种属性。 调用 GetViewType 帮助程序方法来确定视图的 ViewType。 然后会将新视图添加到名为 viewsToCreate 的视图列表。

  4. 在列表的视图集合中使用 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 => 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. 为源中的每个文件创建 newUrl,后者存储文件的新 URL。 可通过将源根文件夹替换为目标根文件夹来创建 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();
}

注意

上面的代码展示了如何迁移列表的根文件夹中存储的文件。 如果列表中包含子文件夹,必须添加其他代码迁移子文件夹及其内容。 如果列表使用工作流,还需要使用附加代码将工作流与新列表相关联。

另请参阅