Обработчики событий NHibernate

В этом примере рассмотрен сценарий обработки событий:

  1. OnPreUpdate (проверка на наличие заполненного поля Годовой оборот в объекте Контрагент).
  2. OnPostUpdate (запись в активность по контрагенту изменение имени контрагента).
  3. OnPreUpdateCollection (запись в активность по контрагенту добавленные/удаленные телефоны).
  4. 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; } }

Ссылки на элементы API

Прикрепленные файлы