30 июл. 2012 г.

Как привязать к списку свои ASPX-формы

Как быстро и просто программным способом назначить для списка свои формы, лежащие в _layouts? Для этого нужно взять у списка нужный тип содержимого и подменить часть XML в его определении. Для примера возьмём первый попавшийся тип содержимого и назначим ему формы просмотра и редактирования:

var ct = list.ContentTypes[0];

string ns = "http://schemas.microsoft.com/sharepoint/v3/contenttype/forms/url";
var xml = new System.Xml.XmlDocument();
xml.LoadXml(
"<FormUrls xmlns=\"" + ns + "\">" +
    "<Display>_layouts/MyDisplayForm.aspx</Display>" +
    "<Edit>_layouts/MyEditForm.aspx</Edit>" +
"</FormUrls>");

ct.XmlDocuments.Delete(ns);
ct.XmlDocuments.Add(xml);
ct.Update();

19 июл. 2012 г.

Рабочий процесс не открывается в дизайнере

Некоторое время не мог понять, почему один рабочий процесс перестал открываться в дизайнере: при открытии отображались несколько сообщений об ошибках типа
The service 'System.Workflow.ComponentModel.Compiler.ITypeProvider' must be installed for this operation to succeed. Ensure that this service is available.
The type 'System.Workflow.Activities.SequentialWorkflowActivity' has no property named 'CanModifyActivities'.
The service 'System.Workflow.ComponentModel.Design.IIdentifierCreationService' must be installed for this operation to succeed. Ensure that this service is available.
Оказалось, такая ошибка появляется, если в проекте есть CS-файл, который называется так же, как файл с классом РП.

5 июл. 2012 г.

Обновление рабочих процессов

Фактически никакой версионности в рабочих процессах (далее - РП) Visual Studio, в отличие от декларативных РП, не существует. То, что в различных статьях называется обновлением рабочего процесса, по сути представляет из себя копирование и развёртывание ещё одного РП. Так называемая новая версия связана со старой только посредством идентификатора, а названия и имена классов могут различаться. Никаких атрибутов для хранения номера версии в определении РП (workflow template) нет, и пользователь никогда не узнает, какой версией какого процесса он пользуется, если, конечно, разработчик не упомянет версию в названии или описании РП.

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

Итак, три необходимых условия для осуществления обновления РП:
  1. Разный код для разных версий РП.
  2. Определения разных версий РП должны иметь одинаковый идентификатор.
  3. Определения разных версий РП должны находиться решениях с разными идентификаторами.
Первый принцип можно реализовать по-разному: создать разноименные классы в одной сборке или одноименные классы в разных сборках.
Остальные параметры решения: идентификаторы и названия возможностей, название и описание определения РП и пр. - можно менять или оставить прежними, на обновление это не влияет.

Я решил записать алгоритм одной из возможных реализаций изменения проекта для корректного обновления РП.

Шаг 1. Изменить идентификатор решения. 
В Visual Studio меняем свойства Solution Id у нашего WSP.

Шаг 2. Изменить версию сборки. 
Если версия сборки меняется при каждом билде, то это хорошо. Если же не меняется, то придётся поменять версию сборки вручную, чтобы новая версия работала вместе со старой.

Шаг 3. Добавить старую сборку в решение. 
Кто не знает, как это сделать, может прочитать инструкцию http://msdn.microsoft.com/ru-ru/library/ee231595.
При добавления сборки нужно в поле Location к имени файла добавить название подпапки (получится, например, "V1\MyWorkflow.dll"), потому что Visual Studio не добавит в решение два одноименных файла: старую сборку и текущую.

Шаг 4. Изменить определение РП. 
Этот шаг не обязателен. В атрибуте CodeBesideAssembly обычно указывается переменная $assemblyname$, так что прописывать новую версию сборки не потребуется. Можно изменить атрибут Name, добавив в название РП номер версии, исключительно для информирования пользователей.
Важно, что нельзя менять идентификатор определения - атрибут Id. Определения РП с одинаковым идентификатором, но находящиеся в разных решениях, SharePoint рассматривает как разные версии одного определения и подменяет старую версию новой. Если же определения находятся в одной возможности или в одном решении, то установится только первое.


Повторяясь, напомню, что этот алгоритм не единственно возможный. Хоть во всех статьях об обновлении РП и пишут о необходимости создания новой сборки, наличие этой новой сборки для развёртывания новой версии РП, строго говоря, не обязательно. Конечно, такой вариант грамотней и предпочтительней, но ведь можно пойти другим путём - создать копию класса РП в текущей сборке! Тогда выполнять шаги 2 и 3 не потребуется, а на шаге 4 вместо атрибута CodeBesideAssembly нужно будет поменять атрибут CodeBesideClass. Способ с копированием класса даже проще и быстрее и лучше подходит для ситуации, когда обновилась не только сборка с РП, но ещё десяток-другой связанных сборок, которые придётся копировать вместе со сборкой РП. Или, например, в сборке с РП находятся другие элементы: веб-части, приёмники событий и т.д. - для которых смена версии сборки влечёт необходимость дополнительных действий при обновлении.


Действия при обновлении РП следующие:
  1. Отозвать старое решение.
  2. Развернуть новое.
  3. Активировать возможность.
  4. Обновить существующие ассоциации с РП, включив их, потому что при деактивации старой возможности ассоциации отключаются.