Подписка на обновление объектов - QualitySolution/QSProjects GitHub Wiki

В основную библиотеку QS.Project при работе встроен механизм подписки на уведомления об изменении(сохранении в БД) объектов доменной модели. Эти уведомления работают только при сохранении объекта внутри того же приложения и только при работе через UnitOfWork.

Настройка DI

Для того чтобы это работало контейнер нужно настроить так:

services.AddEntityChangeWatcher();

Класс EntityChangeDiWatcher позволяет не отписываться от событий вручную, отписка будет происходить при закрытии скоупа.

Подписка

В случае одновременного сохранения нескольких объектов вы получите всего одно уведомление, с перечнем всех измененных объектов подходящих под ваши критерии отбора внутри одного комита. Можно подписываться сразу на изменение нескольких классов одновременно, придет тоже одно событие с общим списком измененных объектов.

Критерии отбора

При подписке на изменения, основной критерий отбора это конечно же тип класса, изменения которого нас интересует. Для подписки с фильтрацией только по типу класса существует различные варианты метода BatchSubscribeOnEntity с подпиской на один или несколько типов классов.

Более сложная фильтрация доступна через вызов BatchSubscribe в виде Fluent интерфейс. Если вызвать BatchSubscribe без дополнительных условий то он сработает как подписка на все события аналогично BatchSubscribeOnAll.

Доступны следующие возможности:

  • ExcludeUow - добавляем конкретные uow в исключения уведомлений.
  • OnlyForUow - подписываемся только на конкретные uow.
  • IfEntity<TEntity> - фильтрация по типу объекта
    • AndChangeType(TypeOfChangeEvent typeOfChange) - дополнительно нас интересуют только конкретные типы изменений, например добавление, обновление или удаление. Можно вызвать несколько раз, если надо указать несколько типов.
    • AndDiffAnyOfProperties(params Expression<Func<TEntity, object>>[] properties) - здесь можно задать список свойств на изменение которых надо реагировать. Если список задан будут приходить только события затрагивающие изменение указанных свойств.
    • AndWhere(Func<TEntity, bool> predicate) - любое условия из полей самого объекта.
    • Or - Возврат на уровень вверх, чтобы можно было указать условия для другого типа объекта.

Жизненный цикл.

Обратите внимание, что все глобальные подписки на любые уведомления держат ссылку на подписавшийся класс, а это означает только одно, этот класс не будет зачищен сборщиком мусора. Поэтому все созданные UnitOfWork и глобальные подписки нужно закрывать при завершении диалога, который их создал. Мы в механизме подписок специально не используем обычные события, а используем ссылку на метод, только для того чтобы, внутри преобразовать ее в мягкую ссылку, которая может быть зачищена сборщиком мусора. Это должно защищать от утечек памяти, если программист забудет отписываться от событий. Но это скорее запасной вариант, поэтому не забывайте при окончании работы класса, подписавшегося на событие отписываться от него. Если напрямую используете AppLevelChangeListener если используется IEntityChangeWatcher через DI, то ничего дополнительно делать не нужно, отписка произойдет автоматически при dispose класса EntityChangeDiWatcher. Ниже пример ручной отписки при использовании AppLevelChangeListener.

public override void Dispose()
{
	EntityChangeWatcher.UnsubscribeAll(this);
	base.Dispose();
}

Примеры подписок

Подписка на один тип класса.

EntityChangeWatcher.BatchSubscribeOnEntity<TEntity>(ExternalEntityChangeEventMethod);

Подписка на несколько типы классов, переданные в массиве любой размерности

EntityChangeWatcher.BatchSubscribeOnEntity(OnEntitiesUpdated, entityTypes);

Пример подписки только на события удаления

EntityChangeWatcher.BatchSubscribe(subscruber.Handler)
	.IfEntity<SimpleEntity>().AndChangeType(TypeOfChangeEvent.Delete);

Пример подписки на события объектов с фильтрацией по значению поля.

EntityChangeWatcher.BatchSubscribe(subscruber.Handler)
	.IfEntity<SimpleEntity>()
	.AndWhere(x => x.Text.Contains("1"))
	.AndWhere(x => x.Text.Contains("2"));

Пример подписки с условиями для нескольких типов объектов

EntityChangeWatcher.BatchSubscribe(subscruber.Handler)
	.IfEntity<SimpleEntity>()
	.AndWhere(x => x.Text.Contains("1"))
	.Or.IfEntity<SimpleEntity2>()
	.AndWhere(x => x.Text.Contains("2"));

Пример подписки на события все события с SimpleEntity за исключением сделанных в uow1 и uow2

EntityChangeWatcher.BatchSubscribe(subscruber.Handler)
	.ExcludeUow(uow1, uow2)
	.IfEntity<SimpleEntity>();
⚠️ **GitHub.com Fallback** ⚠️