Обновление уже развернутых типов контента SharePoint для обработки дополнительных событий элемента
У меня есть тип контента сайта, который использовался для нескольких списков в моем семействе сайтов. В этом типе контента я описываю приемник событий для обработки события ItemAdding. Это отлично работает. Теперь мне нужно обновить тип контента, чтобы ItemUpdating также обрабатывался. В общем, я попытался просто изменить xml для своего типа контента, так как это, казалось, позволяло легко отслеживать версии. Это работало в том смысле, что мои обновления были применены к типу контента сайта, но не к моим спискам, которые использовали этот тип контента. Это было много ожидалось. Затем я заметил, что SharePoint SDK смотрит на это мрачно:
Ни при каких обстоятельствах не обновляйте файл определения типа контента для типа контента после того, как вы установили и активировали этот тип контента. Службы Windows SharePoint Services не отслеживают изменения, внесенные в файл определения типа контента. Следовательно, у вас нет способа отменить изменения, внесенные в типы контента сайта, в дочерние типы контента.
Затем SDK указывает на пару разделов, в которых описано, как использовать пользовательский интерфейс или код для отправки изменений. Так как пользовательский интерфейс не предлагает подключения к получателям событий, я думаю, я буду выбирать путь к коду.
Я думал, что смогу сделать что-то вроде этого и просто добавить новый приемник событий в копию списка типа контента:
SPList list = web.Lists["My list"];
SPContentType ctype = list.ContentTypes["My content type"];
// Doesn't work -- EventReceivers is null below.
ctype.EventReceivers.Add(SPEventReceiverType.ItemUpdating,
"My assembly name", "My class name");
Но подвох в том, что ctype.EventReceivers здесь равен нулю, хотя ItemAdding уже подключен к этому списку. Похоже, что он был перемещен в сам список. Итак, в списке есть действительная коллекция EventReceivers.
SPList list = web.Lists["My list"];
list.EventReceivers.Add(SPEventReceiverType.ItemUpdating,
"My assembly name", "My class name");
Итак, у меня есть пара вопросов:
- Правильный ли способ сделать это, просто добавить новые получатели событий прямо в список и совсем забыть о моем типе контента?
- Чтобы добиться этого изменения, как лучше всего справиться с этим с точки зрения управления конфигурацией? Должен ли я создать простое консольное приложение, чтобы найти все соответствующие списки и изменить каждый из них? Или как-то лучше создать функцию? В любом случае, кажется, что это изменение будет само по себе и его трудно обнаружить будущим разработчикам, которым, возможно, придется работать с этим типом контента.
3 ответа
Вы вызвали ctype.Update(true) после добавления EventReceiver? Если вы этого не сделаете, это не будет сохраняться. И не используйте тип содержимого List, используйте взамен SPWeb.ContentTypes.
Этот код работает для меня:
var docCt = web.ContentTypes[new SPContentTypeId("0x0101003A3AF5E5C6B4479191B58E78A333B28D")];
//while(docCt.EventReceivers.Count > 0)
// docCt.EventReceivers[docCt.EventReceivers.Count - 1].Delete();
docCt.EventReceivers.Add(SPEventReceiverType.ItemUpdated, "ASSEMBLYNAME, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c5b857a999fb347e", "CLASSNAME");
docCt.Update(true);
Параметр true означает, что он также распространяется на все дочерние типы контента. (т.е. для всех списков, использующих тип контента).
Чтобы ответить на вторую часть вашего вопроса, это сложная вещь из-за того факта, что изменения типов содержимого в коллекции сайтов не будут перенесены в списки, в которых они используются. "Копия" по сути состоит из полей в коллекции сайтов, и после добавления типа контента в список больше нет связи между ними. Я думаю, это связано с тем, что вы должны вносить изменения в списки, не влияя на коллекцию сайтов. Во всяком случае, мой вклад в эту "проблему" и то, как я ее решил, включает превращение XML в "мастер", и в компоненте-приемнике я поднимаю XML и нахожу все места, где используется тип контента, и оттуда обновляю типы контента (действительно fieldrefs) на уровне списка, чтобы соответствовать тому в xml. Код выглядит примерно так:
var elementdefinitions = properties.Feature.Definition.GetElementDefinitions();
foreach (SPElementDefinition elementDefinition in elementdefinitions)
{
if (elementDefinition.ElementType == "ContentType")
{
XmlNode ElementXML = elementDefinition.XmlDefinition;
// get all fieldrefs nodes in xml
XmlNodeList FieldRefs = ElementXML.ChildNodes[0].ChildNodes;
// get reference to contenttype
string ContentTypeID = ElementXML.Attributes["ID"].Value.ToString();
SPContentType ContentType =
site.ContentTypes[new SPContentTypeId(ContentTypeID)];
// Get all all places where the content type beeing used
IList<SPContentTypeUsage> ContentTypeUsages =
SPContentTypeUsage.GetUsages(ContentType);
}
}
Следующая вещь - сравнить fieldrefs в xml xml с полями в списке (выполненными атрибутом ID) и убедиться, что они равны. К сожалению, мы не можем обновить все вещи в классе SPFieldLink (fieldref) и (да, я знаю, что это не поддерживается) здесь я фактически использовал отражение для обновления этих значений (например, ShowInEditForm).
Что касается второй части вашего вопроса, я хотел рассказать о том, что мы сделали для подобных ситуаций в прошлом. В нашей ситуации нам потребовалось несколько различных сценариев: один, который позволил бы нам распространять обновления типов контента по всем спискам на всех веб-сайтах, а другой - для сброса основных страниц / макетов страниц в определение сайта (не настраиваемая форма).).
Итак, мы создали несколько пользовательских команд stsadm для каждого из этих действий. Делать это таким образом приятно, потому что сценарии могут быть помещены в систему управления версиями, и это реализует уже существующий интерфейс stsadm.