logo

[ELMA3] Обработчики событий 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 имеет следующие методы:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/// <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)

Пример класса точки расширения

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
[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

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