Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Повторяемость и временное отключение приемников событий в SharePoint

Anna | 17.06.2014 | нет комментариев

Как Вы можете знать, в SharePoint существует уйма приемников событий (EventReceiver), которые разрешают вызывать пользовательский код при выполнении стандартных операций с объектами SharePoint, скажем добавление/удаление/изменение элементов списков. Работа с приемниками событий заслуживает отдельной статьи, но их по данной тематике и так достаточно много, скажем здесь.

Сегодня мы разглядим 2 частных случая задач, которые могут появиться у начинающего разработчика SharePoint при работе с ресиверами:

1. Цикличный вызов событий.

Достаточно легко дозволено представить обстановку, когда ресивер может “загнать” себя в безграничный цикл. Для примера возьмем приемник событий для элемента списка “После обновления” (ItemUpdated). Ваш код исполняет добавочные действия с данным элементом (скажем, на основе введенных пользователем данных, изготавливаете добавочные вычисления и записываете их в требуемое поле), позже чего, безусловно же, вызываете Update для сохранения данных. Позже вызова что должно случиться? Безусловно. Вновь вызовется Ваш код, тот, что был описан в ресивере. И так безмерно.

public override void ItemUpdated(SPItemEventProperties properties)
{
	base.ItemUpdated(properties);
	SPListItem Item = properties.ListItem;

	Item["Title"] = DateTime.Today.ToString("dd.MM.yyyy");
	Item.Update();
}

По непонятным мне причинам, часть разработчиков считает, что для отключения выполнения обработчиков событий довольно вызывать SystemUpdate взамен Update у объекта. Но это не так. Немного того, что наmsdn говорится о том, что данный способ разрешает обновлять элементы списка без обновления полей Изменено (Modified), Кем изменено(ModifiedBy) и увеличению версии элемента, так и простым экспериментальным путем доказывается обратное.

То есть применение SystemUpdate для отключения вызова обработчиков событий будет не довольно, правда, сознаюсь, в вышеуказанном примере как раз таки отличнее вызывать SystemUpdate взамен Update, Дабы исключить метаморфозы вышеуказанных полей и созданию новой версии элемента, если включена версионность.

Данную задачу поможет нам решить качество EventFiringEnabled у SPEventReceiverBase (либо даже у нынешнего наследуемого объекта от SPEventReceiverBase).

Данное качество разрешает руководить вероятностью вызова каких-либо обработчиков событий унаследованных от класса SPEventReceiverBase в нынешнем потоке. То есть в Вашем коде Вы можете выключать и включать вызовы обработчиков событий и не опасаться, что иной код либо простая работа пользователя (которая также вызывает ресиверы) будет омрачена отключением либо выключением обработчиков в Вашем определенном способе.

public override void ItemUpdated(SPItemEventProperties properties)
{
	base.ItemUpdated(properties);
	SPListItem Item = properties.ListItem;
	this.EventFiringEnabled = false;
	Item["Title"] = DateTime.Today.ToString("dd.MM.yyyy");
	Item.Update();
	this.EventFiringEnabled = true;
}

При вызове указанного кода при выполнении способа вызов ресиверов будет отключен. Стоит обратить внимание на то, что все ресиверы, унаследованные от SPEventReceiverBase, при установке свойстваEventFiringEnabled в false будут отключены и об этом нужно будет позаботиться самим. То есть Если в интервале кода между выключением вызова ресиверов и включением Вы попытаетесь обновить иной список, к которому привязан обработчик событий на обновление, он не выполнится (Отчего? Об этом немножко ниже). Код, тот, что должен был быть исполненным, нужно исполнить принудительно.

Если обратить внимание на указанный выше код, то станет сразу ясно, что он не оптимален. Как минимум логичнее обернуть его в try-catch-finally(ну либо как минимум легко try-finally). Вы же знаете что это либоэто?

В итоге получится дальнейший код, тот, что самостоятельно от итога на выходе включит выполнение обработчиков событий обратно

<codeclass="cs">public override void ItemUpdated(SPItemEventProperties properties)
{
	base.ItemUpdated(properties);
	try
	{
		SPListItem Item = properties.ListItem;
		this.EventFiringEnabled = false;
		Item["Title"] = DateTime.Today.ToString("dd.MM.yyyy");
		Item.Update();
	}
	finally
	{
		this.EventFiringEnabled = true;
	}
}

Ниже по тексту будет представлен еще один допустимый вариант отключения с инструкцией using

2. Надобность отключения событий при выполнении определенных операций вне ресивера.

Как отключить выполнение ресиверов внутри самого ресивера мы разобрались. Но дюже часть возникает надобность отключить выполнение ресиверов вне самого ресивера. То есть Вы, скажем, в консольном приложении хотите обновить элемент списка, но не хотите Дабы вызывался ресивер, тот, что обработает данные в нем и перезапишет его, произвести нужные манипуляции с элементом и тогда теснее обновить с ресивером.

По своей сути задача дюже схожа с предыдущей, но у нас нет объекта SPEventReceiverBase и отключать вызов ресиверов нечему. Здесь нужно обратиться к рефлетору и посмотреть, что из себя представляет данный класс и как нам дальше быть.

protected bool EventFiringEnabled
{
  get
  {
	return !SPEventManager.EventFiringDisabled;
  }
  set
  {
	SPEventManager.EventFiringDisabled = !value;
  }
}

Из кода видно, что у класса есть качество EventFiringEnabled (мы им чуть ранее пользовались для отключения вызова событий) и с его поддержкой получается либо устанавливается значение из статического свойства SPEventManager.EventFiringDisabled. Код данного свойства представлен ниже:

internal static bool EventFiringDisabled
{
  get
  {
	SPEventManager.EnsureTlsEventFiringDisabled();
	object data = Thread.GetData(SPEventManager.m_tlsEventFiringDisabled);
	return data != null && (bool) data;
  }
  set
  {
	SPEventManager.EnsureTlsEventFiringDisabled();
	Thread.SetData(SPEventManager.m_tlsEventFiringDisabled, (object) (bool) (value ? 1 : 0));
  }
}

Так как способ статический, а внутренняя работа основана теснее на потоках, то получается, что всякий унаследованный от SPEventReceiverBase класс работает теснее не вовсе с контекстом нынешнего объекта. Желательная информация читается и записывается в отведенные для наших потоков ячейках памяти. Получается, что не столько значимо из какого ресивера выполняется отключение либо включение вызовов других обработчиков, сколько значимо в каком потоке это делается. Таким образом, довольно сделать личный класс, унаследованный от SPEventReceiverBase (либо от SPItemEventReceiver для нашего определенного случая, тот, что в свою очередь также является преемником ), в требуемом месте кода инициализировать экземпляр и трудиться с теснее привычным нам свойством EventFiringEnabled.

public class DisableItemEvents : SPItemEventReceiver
{
	public bool CustomEventFiringEnabled
	{
		get { return base.EventFiringEnabled; }
		set { base.EventFiringEnabled = value; }
	} 
}

И вызывать, скажем, дальнейшим образом:

var EventsDisable = new DisableItemEvents();
try
{
	Item["Title"] = DateTime.Today.ToString("dd.MM.yyyy");
	EventsDisable.CustomEventFiringEnabled = false;
	Item.Update();
}
finally
{
	EventsDisable.CustomEventFiringEnabled = true;
}

Получилось то, чего мы и ждали. При обновлении не отработал ресивер. И как я сказал ранее, данный подход имеет право на жизнь и многие таким пользуются, но данную конструкцию я бы описал немножко напротив. Мне дюже нравятся using-паттерны. Как минимум за то, что применяя данный функционал дозволено быть уверенным, что самостоятельно от ошибок внутри инструкции, неизменно будет вызван Dispose.

Свой класс я описал дальнейшим образом:

public class DisableItemEvents : SPItemEventReceiver, IDisposable
{
	private bool _EventStatus;

	public DisableItemEvents()
	{
		_EventStatus = base.EventFiringEnabled;
		base.EventFiringEnabled = false;
	}

	public void Dispose()
	{
		base.EventFiringEnabled = _EventStatus;
	}

}

Работа с данным классом получается дальнейшей:

using (new DisableItemEvents())
{
	Item.Update(); // Вызов ресиверов НЕ происходит
}
Item.Update(); // Вызов ресиверов происходит



Источник: programmingmaster.ru
Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB