Обработчики событий элементов списка  это классы, унаследованные от SPItemEventReceiver. В классе содержится много методов, почти все они делятся на две группы: Pre-события – методы, оканчивающиеся на -ing, и Post-события – методы, оканчивающиеся на -ed. Все события принимают один аргумент экземпляр класса SPItemEventProperties.

Это теоретический минимум, который необходимо знать для начала.

Коварные AfterProperties и BeforeProperties

Класс SPItemEventProperties содержит пару свойств: AfterProperties и BeforeProperties. На первый взгляд очень хорошие свойства, которые позволяют с небольшими усилиями реализовать множество сценариев. Но на деле все не так…

  1. Для начала необходимо запомнить, а потом распечатать и повесить на стену, на холодильник, да доску и на все остальные поверхности следующие таблицы:
    Список BeforeProperties AfterProperties properties.ListItem
    ItemAdding Пусто Новые значения null
    ItemAdded Пусто Новые значения Новые значения
    ItemUpdating Пусто Новые значения Старые значения
    ItemUpdated Пусто Новые значения Новые значения
    ItemDeleting Пусто Пусто Старые значения
    ItemDeleted Пусто Пусто null

    Библиотека BeforeProperties AfterProperties properties.ListItem
    ItemAdding Пусто Пусто null
    ItemAdded Пусто Пусто Новые значения
    ItemUpdating Старые значения Новые значения Старые значения
    ItemUpdated Старые значения Новые значения Новые значения
    ItemDeleting Пусто Пусто Старые значения
    ItemDeleted Пусто Пусто null

    Поведение списков и библиотек документов разное и отличается от предполагаемого.
    Как видите из таблиц выше, BeforeProperties почти бесполезное свойство.
  2. AfterProperties и BeforeProperties содержат свойство ChangedProperties. Это свойство показывает измененные поля в самой коллекции, а не в элементе.
  3. Свойство-индексатор AfterProperties и BeforeProperties принимает Internal Name поля, а не Display Name как в properties.ListItem.
  4. AfterProperties и BeforeProperties поддерживают нетипизированный IEnumerable, но нигде в документации не указано какого типа возвращается перечисление. Опыты показывают что возвращается DictionaryEntry.
  5. AfterProperties и BeforeProperties возвращают все значения в виде строк. Кроме того для boolean типа поля может быт возвращено "-1" в качестве значения, а строки возвращаются в универсальном формате и при парсинге автоматически переводятся в текущую локаль (+3 часа обычно получается).
  6. При изменении элемента списка в коде в AfterProperties попадают только измененные значения, а при сохранении формы – все значения формы.
  7. При сохранении формы с RichText полем в AfterProperties попадает HTML со всеми заглавными буквами в названиях тегов.

Отмена действия

Pre-события позволяют отменить действие. Для этого необходимо в properties.Cancel присвоить true, присвоить необходимые значения свойствам properties.Status и properties.ErrorMessage. Но и тут есть особенности:

  1. Если вы собираетесь отменить действие, то не вызывайте базовую реализацию метода-обработчика. Иначе отмена не произойдет.
  2. Если вы поставите статус CancelWithError, то будет выкинуто стандартное исключение, которое в режиме отладки отображается желтым экраном смерти.
  3. Если хотите показать свое сообщение об ошибке, то сделайте CancelWithRedirect, но учтите что в таком случае управление не вернется к вызывающему коду.
  4. Если необходимо выполнение разных способов отмены, то анализируйте SPContext и его свойства FormContext и ViewContext.
  5. Отмена работает всегда, для любой учетной записи, в том числе системной. Желательно позволять выполнять действие (не отменять его) администратору коллекции сайтов и учетной записи "SHAREPOINT\system".

Конкурентное выполнение обработчиков событий

В этом посте описан пайплайн обработки событий, картинка ниже кратко его иллюстрирует.

Как видно обработчики Post-событий могут запускаться параллельно. Если будут параллельно запускаться несколько обработчиков, изменяющих сам элемент, то может появиться состояние гонки. Класс SPListItem снабжен механизмом так называемой “оптимистичной конкуренции”. В случая если другой потом успел поменять значение в базе между моментом считывания и записью данных, то выпадет исключение. Но исключения в пост-обработчиках не очень эффективны, так как нет возможности как-либо сигнализировать об ошибке, если только это не синхронный обработчик.

  1. По возможности не изменяйте элемент списка из асинхронного пост-обработчика.
  2. Перехватывайте SPListDataValidationException. Если поймали такое исключение, то выполните properties.InvalidateListItem, а потом снова код обновления элемента.
  3. Используйте следующий блок кода в пост-обработчиках, чтобы не вызвать их циклического запуска
    try
    {
        this.EventFiringEnabled = false;
        //...здесь вызов Update...
    }
    finally
    {
        this.EventFiringEnabled = true;
    }
  4. Также можно использовать SystemUpdate, чтобы не вызывать обработчики событий и не менять время последнего изменения элемента.

К сожалению это далеко не все грабли, которые встречаются при обработке событий. С прочими граблями можно ознакомиться тут:http://msdn.microsoft.com/en-us/library/aa979520.aspx

Теги : SharePoint