6.3.11 Работа с событиями
При работе в системе «ДЕЛО» происходят различные события, сведения о которых сохраняются в базе данных. При работе триггеров БД или ФЗ «Планировщик задач Базы Данных» происходит заполнение таблицы EVNT_FEED всеми происходящими в системе событиями. Записи таблицы содержат ограниченный набор данных о событии:
- Тип события: создание или модификация (в т.ч. удаление) объекта
- Идентификатор объекта события
- Группа документов объекта (для РК, РКПД, поручений)
- Флаги
- Метка времени
- Идентификатор события
Подробно механизм событий описан в Приложение Л.
Для работы ФЗ с событиями, выполняются запросы к БД с критериями поиска необходимых событий, которые будут в дальнейшем обрабатываться. Чтобы сохранять информацию об уже обработанных событиях в ФЗ, сохраняется запись в CUSTOM_STORAGE – ватермарка, со значением номера (ISL_EVENT) последнего события, прошедшего обработку. Поиск событий для последующей обработки осуществляется начиная с данной ватермарки. Если предполагается, что ФЗ будет иметь несколько экземпляров работы или запущена с нескольких хостов, подключенных к одной БД, для каждого экземпляра нужна своя ватермарка.
Пример сохранения значения ватермарки:
|
// Сохранение значения ватермаркиvar customStorage = _dataContext.CreateCustomStorage();
customStorage.ValueId = WATERMARK_ID;
customStorage.Value = value.ToString();
customStorage.OwnerKind = 0;
customStorage.OwnerId = null;
customStorage.OrderNum = 1;
_dataContext.Add(customStorage);
var resSave = _dataContext.SaveChangesAsync().Result;
|
Пример получения значения ватермарки:
|
// Получение значения ватермарки
var customStorage = _dataContext.CustomStorages.Where(p => p.ValueId == WATERMARK_ID).AsNoTracking().SingleOrDefaultAsync();
var value = customStorage.Value;
|
Пример работы с событиями, для наследника BaseDaemon (в BaseEventDispatcher работа с событиями уже реализована):
|
// Работа с событиями для наследника BaseDaemonprivate readonly string WATERMARK_ID = typeof(TestDispatcher).FullName + ".Watermark";
private const int SIZE = 500;
protected override async void Main()
{
DelayInSec = 30;
MarkInfoLaunch("Запуск тестовой ФЗ");
while (true)
{
CheckCancel();
try
{
MarkInfoStart();
var watermark = LoadWatermark();
var events = GetNextEvents(watermark);
if (events.Any())
{
MarkInfoMessage("Обработка событий");
var aggregate = events.Where(e => e.Flags.Contains("I_REF_SEND"))
.OrderBy(e => e.IslEvent)
.ToList();
//Обработка событий
var lastEvent = events.LastOrDefault();
watermark = lastEvent == null ? watermark : (decimal)lastEvent.IslEvent + 1;
SaveWatermark(watermark);
}
}
catch (Exception ex)
{
MarkInfoError(ex);
}
finally
{
SetFinish();
}
MarkInfoMessage("Ожидание событий");
CalculateNextTime();
WaitingProcess();
}
}
private decimal LoadWatermark()
{
using var scope = DaemonsDomain.Instance.ServiceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope();
var db = scope.ServiceProvider.GetRequiredService<IDataContext>();
var customStorage = db.CustomStorages.Where(p => p.ValueId == WATERMARK_ID).AsNoTracking().SingleOrDefaultAsync().Result;
var value = customStorage.Value;
var watermark = String.IsNullOrEmpty(value) ? LastEventISL(db) : decimal.Parse(value);
return watermark;
}
private void SaveWatermark(decimal value)
{
using var scope = DaemonsDomain.Instance.ServiceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope();
var db = scope.ServiceProvider.GetRequiredService<IDataContext>();
var customStorage = db.CustomStorages.Where(p => p.ValueId == WATERMARK_ID).AsNoTracking().FirstOrDefaultAsync().Result;
if (customStorage == null)
{
customStorage = db.CreateCustomStorage();
customStorage.ValueId = WATERMARK_ID;
customStorage.Value = value.ToString();
customStorage.OwnerKind = 0;
customStorage.OwnerId = null;
customStorage.Ordernum = 1;
db.Add(customStorage);
}
else
{
db.EnsureTracked(customStorage);
customStorage.Value = value.ToString();
}
db.SaveChanges();
}
private List<IEvntFeed> GetNextEvents(decimal watermark)
{
using var scope = DaemonsDomain.Instance.ServiceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope();
var db = scope.ServiceProvider.GetRequiredService<IDataContext>();
return db.EvntFeeds.Where(p => p.KindObject == 1 && p.KindEvent == FeedEventKind.Creation)
.AsNoTracking()
.OrderBy(p => p.IslEvent)
.ToList();
}
private decimal LastEventISL()
{
using var scope = DaemonsDomain.Instance.ServiceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope();
var db = scope.ServiceProvider.GetRequiredService<IDataContext>();
var l = db.EvntFeeds.Select(p => p.IslEvent).DefaultIfEmpty().Max();
if (l == null)
return 0;
var watermark = Convert.ToDecimal(l);
return watermark;
}
|
Main – метод работы ФЗ. Данный пример для автоматической ФЗ, поэтому указывается задержка DelayInSec, вычисление времени следующего запуска CalculateNextTime и ожидание следующего запуска WaitingProcess. При наличии событий, выполняется отбор событий в aggregate, с флагом “I_REF_SEND”, т.е. с добавлением адресата. Таким образом, в aggregate содержатся события с добавлением адресатов при создании РК.
LoadWatermark – метод загрузки значения ватермарки.
SaveWatermark – метод сохранения значения ватермарки.
GetNextEvents – метод получение событий с созданием РК (KIND_EVENT = 1, KIND_OBJECT = 1), количество событий указано в RecordsPerPage.
LastEventISL – метод получение последнего события, при первом запуске работы ФЗ, при отсутствии ватермарки.
Данный пример учитывает работу только одного экземпляра ФЗ. Если предполагается работа нескольких экземпляров, нужно использовать свою ватерматрку для каждого экземпляра.
Примеры фоновых задач можно посмотреть в дистрибутиве системы в архиве «APISamples.zip» (проект Eos.Delo.Samples.Daemons).
