Перейти к содержанию

Использование TransactionScope для выполнения операций после завершения транзакции

В этом примере мы рассмотрим использование области выполнения транзакции для запуска кастомной логики после завершения транзакции.

Для этого можно использовать свойство TransactionScopeContext.Current. Данное свойство возвращает текущий контекст области выполнения транзакции в случае, если данная область была открыта. Данное свойство доступно во всех расширениях, выполняющихся в транзакции (BeforeCommitTransaction и AfterCommitTransaction) а также в коде шаблонов бизнес-процессов и маршрутов. В иных местах область выполнения транзакции доступна в случае, если код выполняется внутри транзакции. Проверить это можно с помощью свойства TransactionScopeContext.HasCurrent.

У объекта ITransactionScopeContext есть следующие свойства:

  • Handlers - список обработчиков, которые будут выполненые после завершения транзакции. Их выполнение идёт последовательно по списку.

  • Info - дополнительная информация, которая будет передана в каждый из обработчиков.

Каждый обработчик в качестве параметра получает объект контекста с типом ITransactionFinishedContext, который содержит следующие свойства:

  • ValidationResult - результат валидации выполнения транзакции. Можно использовать для получения информации о возникшей ошибке или же дописать в него иные сообщение валидации.

  • Info - дополнительная информация, которая была добавлена в контекст выполнения транзакции.

  • IsCommited - флаг показывает, был ли выполнен коммит транзакции. Если флаг не установлен, значит был выполнен откат транзакции.

  • DbScope - объект для взаимодействия с базой данных.

  • CancellationToken - объект, посредством которого можно отменить асинхронную задачу.

Пример использования TransactionScopeContext.Current для снятия взятой ранее блокировки после завершения транзакции:

using System.Threading.Tasks; using Tessa.Cards; using Tessa.Cards.Extensions; using Tessa.Cards.Extensions.Templates; using Tessa.Extensions.Default.Shared; using Tessa.Platform; using Tessa.Platform.Data;

namespace Tessa.Extensions.Server { public sealed class ExampleStoreExtension : CardStoreExtension { #region Fields

private readonly ICardLockingStrategy cardLockingStrategy;

#endregion

#region Constructors

public ExampleStoreExtension( ICardLockingStrategy cardLockingStrategy) { Check.ArgumentNotNull(cardLockingStrategy, nameof(cardLockingStrategy));

this.cardLockingStrategy = cardLockingStrategy; }

#endregion

#region Base Overrides

public override async Task AfterBeginTransaction(ICardStoreExtensionContext context) { // В качестве примера будем брать блокировку на чтение карточки сателлита типового решения и снимать её после завершения транзакции. // Получаем идентификатор карточки сателлита var satelliteID = await CardSatelliteHelper.TryGetUniversalSatelliteIDAsync( context.DbScope, context.Request.Card.ID, null, DefaultCardTypes.KrSatelliteTypeID, context.CancellationToken);

// Не выполняем никакую логику, если он не задан if (satelliteID is null) { return; }

bool isSuccess; // Берём блокировку в отдельном подключении к базе, чтобы не заблокировать всю строку в Instances await using (context.DbScope.CreateNew()) { (isSuccess, _) = await this.cardLockingStrategy.ObtainReaderLockAsync( satelliteID.Value, context.ValidationResult, context.CancellationToken); }

if (!isSuccess) { return; }

// Добавляем обработчик, который выполнится после завершения текущей транзакции и снимет блокировку на чтение для карточки сателлита. TransactionScopeContext.Current.Handlers.Add(async ctx => { // Не пробрасываем ctx.CancellationToken в метод, чтобы выполнить снятие блокировки, даже выполняется откат транзакции из-за OperationCancelledException. await this.cardLockingStrategy.ReleaseReaderLockAsync(satelliteID.Value, CancellationToken.None); }); }

#endregion } }

Back to top