[ELMA3] Обработчики событий NHibernate
В этом примере рассмотрен сценарий обработки событий:
- OnPreUpdate (проверка на наличие заполненного поля Годовой оборот в объекте Контрагент).
- OnPostUpdate (запись в активность по контрагенту изменение имени контрагента).
- OnPreUpdateCollection (запись в активность по контрагенту добавленные/удаленные телефоны).
- OnPostUpdateCollection (проверка на использование в поле и»символов, отличных от цифр, если такие символы есть, сохранить контрагент не удастся, о чем пользователь будет предупрежден).
Суть Pre событий в том, что при выполнении действий с объектом (добавление/изменение) существует возможность отменить само событие. События Pre можно применить для проверки введенных данных в поля объекта, если данные введены неверно или произошла ошибка, событие можно отменить.
PreUpdateCollection – это единственное событие, где можно получить и сравнить значение коллекции 1-N.
События Post выполняются после обмена данными с БД. Данное событие можно применить для записи истории по объекту, логирования, пересчета данных.
В данном примере будет продемонстрировано, как получить старое/новое значение коллекции, а также как получить старое/новое значение поля объекта.
Все события EntityEventsListener выполняются в момент вызова Save у объекта, следовательно, событие будет выполняться даже в том случае, если в сценариях будет создаваться/изменяться объект при вызове Save. Использование именно EntityEventsListener бывает необходимо, если после изменения объекта в сценариях и его сохранения при помощи Save необходимо выполнение дополнительного кода после сохранения объекта, который указан в EntityEventsListener.
Пример отображения данных
Рис. 1. Ведение истории по полям контрагента и история по коллекции объектов внутри контрагента (Телефон)
Рис. 2. Проверка на использование недопустимых символов в поле «Телефоны»
Методы базового класса EntityEventsListener
Базовый класс EntityEventsListener имеет следующие методы:
/// <summary> /// Перед вставкой /// </summary> /// <param name="event">Параметры события</param> /// <returns><c>true</c>, если нужно первать операцию</returns> public virtual bool OnPreInsert(PreInsertEvent @event) /// <summary> /// После вставки /// </summary> /// <param name="event">Параметры события</param> public virtual void OnPostInsert(PostInsertEvent @event) /// <summary> /// Перед обновлением /// </summary> /// <param name="event">Параметры события</param> /// <returns><c>true</c>, если нужно первать операцию</returns> public virtual bool OnPreUpdate(PreUpdateEvent @event) /// <summary> /// После обновления /// </summary> /// <param name="event">Параметры события</param> public virtual void OnPostUpdate(PostUpdateEvent @event) /// <summary> /// Перед удалением /// </summary> /// <param name="event">Параметры события</param> /// <returns><c>true</c>, если нужно первать операцию</returns> public virtual bool OnPreDelete(PreDeleteEvent @event) /// <summary> /// После удаления /// </summary> /// <param name="event">Параметры события</param> public virtual void OnPostDelete(PostDeleteEvent @event) /// <summary> /// После удаления элемента в коллекции /// </summary> /// <param name="event">Параметры события</param> public virtual void OnPostRemoveCollection(PostCollectionRemoveEvent @event) /// <summary> /// После пересоздания элемента в коллекции /// </summary> /// <param name="event">Параметры события</param> public virtual void OnPostRecreateCollection(PostCollectionRecreateEvent @event) /// <summary> /// После обновления элемента в коллекции /// </summary> /// <param name="event">Параметры события</param> public virtual void OnPostUpdateCollection(PostCollectionUpdateEvent @event) /// <summary> /// Перед удаления элемента в коллекции /// </summary> /// <param name="event">Параметры события</param> public virtual void OnPreRemoveCollection(PreCollectionRemoveEvent @event) /// <summary> /// Перед пересозданием элемента в коллекции /// </summary> /// <param name="event">Параметры события</param> public virtual void OnPreRecreateCollection(PreCollectionRecreateEvent @event) /// <summary> /// Перед обновлением элемента в коллекции /// </summary> /// <param name="event">Параметры события</param> public virtual void OnPreUpdateCollection(PreCollectionUpdateEvent @event) /// <summary> /// Перед загрузкой /// </summary> /// <param name="event"></param> public virtual void OnPreLoad(PreLoadEvent @event)
Пример класса точки расширения
[Component] public class EntityEvent : EntityEventsListener { public override void OnPostUpdateCollection(PostCollectionUpdateEvent @event) { var contr = @event.AffectedOwnerOrNull as IContractor; if (contr != null) { var collection = @event.Collection; var collectionEntry = @event.Session.PersistenceContext.GetCollectionEntry(@event.Collection); var collectionEntries = collection.Entries(collectionEntry.LoadedPersister); var listChars = new List<string> { " ", "-", "(", ")" }; var index = 1; foreach (var entry in collectionEntries) { if (!(entry is IPhone)) continue; var phone = entry as IPhone; long number; var lastNumber = phone.PhoneString; foreach (var str in listChars) {
if(lastNumber != null) lastNumber = lastNumber.Replace(str, string.Empty); } if (!long.TryParse(lastNumber, out number)) throw new Exception(string.Format("Телефон №{0} имеет недопустимые символы", index)); index++; } } } //Запись истории изменения имени контрагента public override void OnPostUpdate(PostUpdateEvent @event) { if (!(@event.Entity is IContractor)) return; var nameIndex = Array.IndexOf(@event.Persister.PropertyNames, "Name"); var contractor = @event.Entity as IContractor; if ((string)@event.OldState[nameIndex] != (string)@event.State[nameIndex]) { var comment = InterfaceActivator.Create<IComment>(); comment.CreationAuthor = UserManager.Instance.Load(SecurityConstants.SystemUserUid); comment.CreationDate = DateTime.Now; comment.Text += string.Format("{2} изменил имя контрагента с {0} на {1}\r\n", @event.OldState[nameIndex], @event.State[nameIndex], contractor.ChangeAuthor.FullName); comment.Save(); contractor.Comments.Add(comment); var actionHandler = Locator.GetServiceNotNull<IEntityActionHandler>(); actionHandler.ActionExecuted(EntityActionEventArgs.TryCreate(null, contractor, ContractorActions.AddComment)); } } //Проверка корректности введенного номера телефона на наличие посторонних символов/букв public override void OnPreUpdateCollection(PreCollectionUpdateEvent @event) { var contr = @event.AffectedOwnerOrNull as IContractor; if (contr != null) { var collectionEntry = @event.Session.PersistenceContext.GetCollectionEntry(@event.Collection); var newCollection = (@event.Collection as IEnumerable).Cast<object>().ToList(); //Новая коллекция var oldCollection = (collectionEntry.Snapshot as IEnumerable).Cast<object>().ToList(); //Старая коллекция var removed = oldCollection.Where(item => !newCollection.Contains(item)).ToArray(); //Удаленные элементы в новой коллекции var added = newCollection.Where(item => !oldCollection.Contains(item)).ToArray(); //Добавленные элементы в новой коллекции if (collectionEntry.Role.EndsWith(".Phone")) { var comment = InterfaceActivator.Create<IComment>(); comment.CreationAuthor = UserManager.Instance.Load(SecurityConstants.SystemUserUid); comment.CreationDate = DateTime.Now; foreach (var add in added) { var phone = add as IPhone; if (phone != null) { comment.Text += string.Format("Добавлен новый телефон {0}\r\n", phone.PhoneString); } } foreach (var rem in removed) { var phone = rem as IPhone; if (phone != null) { comment.Text += string.Format("Телефон {0} был удален\r\n", phone.PhoneString); } } comment.Save(); contr.Comments.Add(comment); var actionHandler = Locator.GetServiceNotNull<IEntityActionHandler>(); actionHandler.ActionExecuted(EntityActionEventArgs.TryCreate(null, contr, ContractorActions.AddComment)); } } } //Проверка на заполненность поля "Годовой доход", если поле не заполнено - событие прерывается public override bool OnPreUpdate(PreUpdateEvent @event) { if (@event.Entity is IContractor) { try { const double zero = 0; var nameIndex = Array.IndexOf(@event.Persister.PropertyNames, "AnnualIncome"); var annualIncomeValue = @event.State[nameIndex]; if (annualIncomeValue != null && !annualIncomeValue.ToString().IsNullOrWhiteSpace() && (double)annualIncomeValue != zero && (double)annualIncomeValue > zero) { Logger.Log.Error("Значение поля годовой доход заполнено корректно"); //Обработка события return false; } Logger.Log.Error("Значение поля годовой доход меньше нуля, ноль или null"); return true; //Прервать событие } catch (Exception ex) { Logger.Log.Error(ex.Message); return true; //Прервать событие } } return false; } }