[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;
}
}