Изменение элемента списка без вызова событий OnItemChanged

Иногда (я бы даже сказал часто) необходима реализовать следующую конструкцию: в wokflow имеется активность (activity) OnWorkflowItemChanges или OnTaskChanged. В этой активности производится какая-то проверка, и по результатам проверки присваивается значение какому-либо полю текущего элемента.
Чтобы было понятнее, приведу пример.
Имеется список со следующими полями:

  • Наименование
  • Статус (Новая, утверждена первым, уверждена вторым, отклонена)
  • Примечание
В списке включена опция "требовать утверждения". Необходимо, чтобы каждый элемент утверждали два человека (для примера пусть это будет один и тот же человек, просто он будет утверждать элемент два раза)
Создан Workflow:
image
whileApproved проверяет состояние переменной this.itemNotApproved.
logToHistoryListActivity записывает в журнал рабочего процесса значение поля "Статус".
Нам необходимо:
— проверить, что элемент утвержден или отклонен
— присвоить соотвественный статус
Вот код, который должен это делать:
            switch (workflowProperties.Item.ModerationInformation.Status)
            {
                case SPModerationStatusType.Approved:
                    if ((string)workflowProperties.Item["Статус"] == "Новая")
                    {
                        workflowProperties.Item["Статус"] = "Утверждена первым";
                        this.itemNotApproved = true;

                        workflowProperties.Item.ModerationInformation.Status = SPModerationStatusType.Pending;

                        workflowProperties.Item.Update();
                    }
                    else
                        if ((string)workflowProperties.Item["Статус"] == "Утверждена вторым")
                        {
                            workflowProperties.Item["Статус"] = "Утверждена";
                            this.itemNotApproved = false;
                            workflowProperties.Item.Update();
                        }
 
                    break;
                case SPModerationStatusType.Denied:
                    workflowProperties.Item["Статус"] = "Отклонена";
                    this.itemNotApproved = false;
                    workflowProperties.Item.Update();
                    break;
            }

Если вы его выполните, то работать будет всё так, как и ожидалось. За одиним маленьким исключением.
Допустим, вы создали элемент, и следом его утвердили. Затем утвердили его еще раз.
Если Вы загляните в журнал рабочего процесса, то увидите, что в нем есть 4 записи:
Статус = Утверждена первым
Статус = Утверждена первым
Статус = Утверждена вторым
Статус = Утверждена вторым
 
Возникает вопрос — почему именно 4? Ответ просто — мы ведь изменили элемент, вызвав метод Update(), и как следствие, каждый раз вызываем новое событие OnItemChanged. У класса SPListItem есть метод UpdateOverwriteVersion(), но и он вызывает событие OnItemChanged.
Возникает вопрос — что же делать? Ответ также прост, как и не очевиден. Если бы мы делали подобную конструкцию в отработчики события списка с помощью features (н-р события того же события OntItemChanged, но уже списка), то всё было бы просто — там все отработчики наледованы от класса SPItemEventReceiver, который имеет методы DisableEventFiring() и EnableEventFiring(), смысл работы которых ясен из названия. Проблема в том, что workflow понятия не имеет об этом классе.
Но есть решение, как исправить сею несправедливость. Нам необходимо создать новый класс, наследумый от SPItemEventReceiver, и создать в нем всего одну функцию, которая на вход будет принимать экземпляр класса SPListItem, выключать генерацию событий с помощью DisableEventFiring(), обновлять элемент, а затем включать генерацию событий с помощью EnableEventFiring().
Вот примерный код такого класса:
class EmptyItemReceiver : SPItemEventReceiver
    {
        public void UpdateDisableEventFiring(SPListItem item)
        {
            this.DisableEventFiring();
            item.SystemUpdate(false);
            this.EnableEventFiring();
        }
 
    }

 
Всё, что нам осталось, это создать экземпляр созданного нами класса, и использовать его функцию UpdateDisableEventFiring.
После всех изменений, код должен быть таким:
EmptyItemReceiver _eventReceiver = new EmptyItemReceiver();
switch (workflowProperties.Item.ModerationInformation.Status)
{
   case SPModerationStatusType.Approved:
           if ((string)workflowProperties.Item["Статус"] == "Новая")
           {
               workflowProperties.Item["Статус"] = "Утверждена первым";
               this.itemNotApproved = true;

               workflowProperties.Item.ModerationInformation.Status = SPModerationStatusType.Pending;

               _eventReceiver.UpdateDisableEventFiring(workflowProperties.Item);
           }
           else
           if ((string)workflowProperties.Item["Статус"] == "Утверждена вторым")
           {
               workflowProperties.Item["Статус"] = "Утверждена";
               this.itemNotApproved = false;
               _eventReceiver.UpdateDisableEventFiring(workflowProperties.Item);
           }
 
       break;
   case SPModerationStatusType.Denied:
        workflowProperties.Item["Статус"] = "Отклонена";
        this.itemNotApproved = false;
        workflowProperties.Item.Update();
        break;
}

 
И после этих изменений всё будет работать так, как ожидается.
 
На последок покажу для тех, кто подумал, что это ерундовая проблема, вот такой пример.
Сделаем так, что бы в поле примечание вносились данные о том, кто и когда изменил элемент. Добавим такие строки в onWorkflowItemChanged_Invoked:

workflowProperties.Item["Примечание"] = String.Format("Изменен {0} {1}",workflowProperties.OriginatorUser.Name,DateTime.Now().ToString());

workflowProperties.Item.Update();

и запустим рабочий процесс. Эффект будет убийственный для системы: workflow просто зациклиться, ведь каждый раз будет вызываться событие OnWorkflowItemChanged, и в нем каждый раз будет происходить изменение элемента. Хорошо, что разработчики позаботились и об этом, и таких итераций будет всего 10.

Реклама

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

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

Логотип WordPress.com

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

Фотография Twitter

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

Фотография Facebook

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

Google+ photo

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

Connecting to %s

%d такие блоггеры, как: