Сохранение идентичности объектов при экспорте и импорте сайтов в Sharepoint в различные позиции в топологии сайт коллекции

Как вы знаете объектная модель Sharepoint-а содержит классы, которые позволяют экспортировать и импортировать сайты, листы, библиотеки документов и др. объекты в Sharepoint. Пожалуй, наиболее важными из них являются SPExport и SPImport. Если вы не знакомы с content deployment API, то перед тем, как продолжить чтение, я советую вам прочитать серию статей Stefan Goßner-а: Deep Dive into the SharePoint Content Deployment and Migration API. Как я сказал, упомянутые классы позволяют экспортировать все объекты сайта (SPWeb) вместе с содержимым (также как сайт коллекцию целиком) и импортировать его в другое место (в другую позицию в топологии текущей сайт коллекции, в другую сайт коллекцию, либо на другой сервер). Вы можете ознакомиться с другими возможностями content deployment API здесь. В этой статье я хочу описать проблему с сохранением идентичности объектов (их уникальных идентификаторов) во время импорта сайта в позицию топологии сайт коллекции, которая отличается от исходной. Проще говоря, когда нам нужно импортировать сайт в другой родительский сайт, сохранив при этом идентификаторы импортируемых объектов. Ниже я опишу, почему это является проблемой.

После того, как мы экспортировали сайт, у нас есть 2 альтернативных пути для импорта сайта:

  • импорт сайта с сохранением идентичности объектов;
  • импорт без сохранения идентичности.

С первым методом все дочерние объекты сайта (напр. листы, библиотеки документов, list items, и др.) сохранят свои уникальные идентификаторы (в форме GUID-ов) и местоположение после импорта такими же, как они имели в исходной сайт коллекции. Этот метод имеет множество преимуществ, т.к. вам не нужно выполнять дополнительных действий после импорта. Так например, у вас не будет головной боли с ретаргетингов content by query веб парты (если вы не знакомы с этой проблемой, я рекомендую ознакомиться с этой статьей Gary Lapointe: Retarget Content Query Web Part). Также у вас не будет проблем с ретаргетингом lookup полей и т.д. Но вместе с достоинствами этот метод также имеет серьезное ограничение: вы должны импортировать экспортированный сайт в ту же позицию (тот же URL) относительно родительской сайт коллекции, что и на исходном сайте. Т.е. если ваш сайт site2 находился имел следующий URL http://example.com/sites/sitecollection/site1/site2, то он должен быть импортирован в то же местоположение на целевом сервере, относительно родительской сайт коллекции http://example1.com/site1/site2, но не может быть импортирован, например, в http://example1.com/site2, где http://example1.com – рутовый сайт сайт коллекции.

Второй метод может быть использован, когда вам надо скопировать сайт в другую позицию в топологии сайт коллекции. Именно он используется в стандартном менеджере сайтов при копировании сайтов, а также в операции stsadm import). Вы можете сами убедиться в последнем утверждении, если откроете утилиту stsadm в рефлекторе и посмотрите код класса SPImportOperation:

public override void Run(StringDictionary keyValues)
{
    ...
    SPImportSettings settings;
    ...
    settings.RetainObjectIdentity = false;
    ...
}

Как видно, он просто устанавливает свойство RetainObjectIdentity = false без каких-либо дополнительных условий и ограничений. Однако второй подход имеет недостатки, которые являются преимуществом первого подхода: с ним у вас будут проблемы с ретаргетингом.

По сути мы должны идти на компромисс, выбирая между этими двумя методами. Возникает вопрос: существует ли еще одно решение, сочетающее в себе достоинства обоих методов, т.е. позволяющие сохранять идентификаторы объектов и не имеющий ограничения на позицию импортируемого сайта в топологии сайт коллекции? Да – такой метод есть, но я должен сразу вас предупредить, что подход, описанный ниже, является по сути хаком и не поддерживается MS. Вам нужно иметь это ввиду, когда вы будете принимать решение использовать этот подход или нет.

Перед тем, как описать непосредственное решение, я хотел бы немного рассказать о причине подобной проблемы. Для этого необходимо более подробно рассмотреть класс SPImport, который не может корректно импортировать сайты, являющиеся потомками других подсайтов в сайт коллекции (т.е. не потомком рутового сайта сайт коллекции). Это происходит потому, что после экспорта веб сайт больше не имеет связи с родительским сайтом (orphaned), т.к. мы не экспортируем его вместе с потомком. Один из путей исправить эту проблему – провести процедуру изменения родительского сайта (re-parenting) во время импорта, используя событие SPImport.Started – см. Deep Dive into the SharePoint Content Deployment and Migration API — Part 3. Но к сожалению, этот метод не работает, если мы установим RetainObjectIdentity = true. Если вы изучили приведенную выше статью, то видели, что для того чтобы идентифицировать все объекты без родителей код использует коллекцию args.RootObjects (где args – объект типа SPDeploymentEventArgs, который является параметром делегата OnImportStarted). Давайте посмотрим, как инициализируется эта коллекция с помощью рефлектора:

public override void Run()
{
    try
    {
        ...
        SPImportObjectCollection rootObjects = this.DeserializeRootObjectMap();
        this.OnStarted(new SPDeploymentEventArgs(SPDeploymentStatus.Started, base.ObjectsProcessed, base.ObjectsTotal, base.DataFileManager.DataFilePath, rootObjects));
        ...
    }
    catch (Exception exception3)
    {
        ...
    }
    ...
}

Итак, коллекция RootObjects – это результат вызова метода DeserializeRootObjectMap(). Давайте посмотрим на этот метод:

private SPImportObjectCollection DeserializeRootObjectMap()
{
    SPImportObjectCollection objects = new SPImportObjectCollection();
    FileInfo rootObjectMapFile = base.DataFileManager.RootObjectMapFile;
    if (!rootObjectMapFile.Exists)
    {
        return objects;
    }
    if (this.Settings.RetainObjectIdentity)
    {
        return objects;
    }
    ...
}

Достаточно выразительно: если мы установили RetainObjectIdentity = true, то всегда возвращается пустая коллекция. Вот почему мы не можем использовать re-parenting объектов, используя событие Started класса SPImport, как это указано в приведенной выше статье.

Я обнаружил следующий способ исправить это. После того, как мы экспортировали сайт, используя класс SPExport, у нас получилось множество xml файлов с определениями объектов (я исхожу из предположения, что мы не использовали сжатие в cab файлы при экспорте). В этих xml файлах множество относительных URL-ов экспортированного сайта, напр. “/site1/site2”. Необходимо заменить все эти вхождения на относительный путь сайта в целевой сайт коллекции. Т.е. если вам нужно импортировать site2 не как подсайт сайта site1 в целевой сайт коллекции, а как подсайт рутового сайта сайт коллекции, то нужно заменить все строки “/site1/site2” на “/site2” во всех файлах, которые получились после экспорта. Проделав эту операцию, мы заменили настоящего родителя сайта site2 (/site1) на рутовый сайт сайт коллекции (/). После этого вы можете использовать SPImport с RetainObjectIdentity = true, и это будет работать так, как если бы вы экспортировали site2 из под рутового сайта сайт коллекции.

Как я уже упоминал, это решение является обходным путем стандартных способов экспорта и импорта. Нет гарантии, что он будет работать во всех случаях. Однако этот подход помог мне в одном из реальных проектов – я смог производить копирование сайтов с использованием SPExport и SPImport в различные позиции в топологии сайт коллекции и обойти проблемы с ретаргетингом объектов Sharepoint-а. Я надеюсь, что он пригодится и в вашей практике разработки под Sharepoint.

Реклама

Об авторе sadomovalex

Старший инженер, team lead, консультант. Работаю в стеке .Net. Последние несколько лет занимаюсь разработкой enterprise приложений под Sharepoint, чему и будет в основном посвящена тематика этого блога. Также активно использую и интересуюсь ASP.Net MVC, DDD, TDD, Agile. Активно участвую в жизни многих профессиональных сообществ, SPb .Net UG, SPb ALT.Net, rsdn, Finland SP UG и др.
Запись опубликована в рубрике Content deployment, Sharepoint. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s