Поделиться через


Замена списков SharePoint, созданных на основе определений списков в решениях фермы

Если вы использовали определения списков для создания списков в решении фермы, узнайте, как преобразовать их в новые решения, которые предоставляют аналогичные функции с помощью клиентской объектной модели (CSOM).

Важно!

Решения фермы нельзя перенести в SharePoint Online. Применив методы и код, описанные в этой статье, вы можете создать новое решение с функциями, аналогичными функциями, предоставляемыми решениями фермы, которое затем можно развернуть в SharePoint Online. Если вы используете подход преобразования на месте, может потребоваться развернуть новое решение в SharePoint Online. Если вы используете подход Swing или миграции содержимого, сторонние средства могут создавать списки за вас. Дополнительные сведения см. в статье Подходы к преобразованию для развертывания новой надстройки SharePoint.

Код, приведенный в этой статье, требует дополнительного кода, чтобы обеспечить полностью работающее решение. Например, в этой статье не рассматривается проверка подлинности для Office 365, реализация необходимой обработки исключений и т. д. Дополнительные примеры кода см. в проекте шаблоны и методики разработчика Office 365.

Примечание.

Код, приведенный в этой статье, предоставляется "как есть" без какой-либо явной или подразумеваемой гарантии, включая подразумеваемые гарантии пригодности для какой-либо цели, для продажи или гарантии отсутствия нарушения прав иных правообладателей.

Совет

Используйте описанные в этой статье методы, чтобы одновременно обновлять только несколько списков. Кроме того, при поиске обновляемых списков не следует фильтровать списки по типу списка.

Чтобы заменить списки, созданные из определений списков, с помощью CSOM:

  1. Поиск списков, созданных с помощью определенного базового шаблона.

  2. Создайте список с помощью встроенного определения списка.

  3. Настройка параметров списка.

  4. Задайте типы контента в новом списке на основе типов контента, заданных в исходном списке.

  5. Добавление или удаление представлений (необязательно).

  6. Перенос содержимого из исходного списка в новый.

Подготовка к работе

В идеале следует просмотреть существующие решения фермы, узнать о методах, описанных в этой статье, а затем спланировать применение этих методов к существующим решениям фермы. Если вы не знакомы с решениями фермы или у вас нет существующего решения фермы для работы, вам может быть полезно:

  1. Ознакомьтесь с разделом Замена списков, созданных на основе пользовательских шаблонов, чтобы получить краткое представление о том, как списки создавались декларативно с помощью определений списков.

    Примечание.

    В Contoso.Intranet в файле elements.xml для SP\ListTemplate\LTContosoLibrary идентификатор пользовательского шаблона списка — 10003. Вы будете использовать его для определения шаблона, который использовался для создания списка. Также просмотрите параметры конфигурации и представления, определенные в списке.

  2. Сведения о решениях фермы. Дополнительные сведения см. в статье Создание решений фермы в SharePoint.

Поиск списков, созданных с помощью определенного базового шаблона

В следующем коде метод показывает, как найти списки, созданные с помощью определенного базового шаблона. Этот метод:

  1. Использует ClientContext для получения всех списков в текущем веб-сайте с помощью Web.Lists. Оператор Include используется в лямбда-выражении для возврата коллекции списков. Все возвращаемые списки должны иметь значения свойств, указанные для BaseTemplate, BaseType и 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);
     }
    

Создание списка

Чтобы создать список, 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. Имя == contentType.Name). Если тип контента отсутствует в новом списке, он добавляется в список.

  5. Загрузка newList во второй раз, так как AddExistingContentType , возможно, изменил типы контента.

  6. Для каждого типа контента в newList определите, соответствует ли тип контента типу содержимого в исходном списке на основе ContentType.Name с помощью listToBeReplaced.ContentTypes.Any(ct => ct. Имя == contentType.Name). Если совпадение не найдено, тип контента добавляется в contentTypesToDelete для удаления из нового списка.

  7. Удаление типа контента путем вызова ContentType.DeleteObject.

Примечание.

Если вы используете подход преобразования на месте и типы контента были развернуты декларативно с помощью Feature Framework, необходимо:

  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. Использование лямбда-выражения для загрузки различных свойств представления в коллекции представлений .

  3. Для каждого представления в исходном списке создайте представление с помощью viewCreationInformation.

    Различные свойства задаются в новом представлении на основе свойств представления из исходного представления. Для определения ViewType представления вызывается вспомогательный метод GetViewType. Затем новое представление добавляется в список представлений с именем viewsToCreate.

  4. Добавление представлений в коллекцию List.Views с помощью метода Add в коллекции 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 == view. Заголовок. Если представление в новом списке не имеет соответствующего представления в исходном списке, добавьте представление в viewsToRemove.

  3. Удаление всех представлений в viewsToRemove с помощью View.DeleteObject.

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

Примечание.

В предыдущем коде показано, как перенести файлы, хранящиеся в корневой папке списка. Если список содержит вложенные папки, необходимо добавить дополнительный код для переноса вложенных папок и их содержимого. Если в списке используются рабочие процессы, для связывания рабочего процесса с новым списком требуется дополнительный код.

См. также