IDbTransactionInterceptor не перехватывает фиксации транзакций в пределах требуемого TransactionScope

Контекст: я пытаюсь опубликовать события базы данных и данные в очереди, но только если данные / транзакция зафиксированы успешно. Мы делаем это, чтобы мы могли запустить последующую логику на этих данных (постобработка). Мы отформатировали наши события как что-то вроде [objecttype][action] (например: userAdded, userDeleted и т. Д.). Я не хочу запускать вызов SaveChanges() просто потому, что существует вероятность того, что есть несколько вызовов SaveChanges() (заключенных внутри TransactionScope), где соответствующей логике постобработки могут потребоваться данные из более поздних вызовов SaveChanges(). Я не хочу иметь дело с условиями гонки, поэтому я хочу дождаться фиксации транзакции и отправить событие в очередь, когда это произойдет.

Попытка решения: я создал реализацию IDbTransactionInterceptor и добавил ее в наши перехватчики при запуске. Я реализовал только метод void Committed(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext) с нашей логикой (остальные методы просто пусты).

Проблема: метод Committed в перехватчике никогда не используется. Транзакция выполняется правильно (в случае ошибки в БД ничего нет). TransactionScope настроен как new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)

Если я переключаю область действия на «Подавлено» (TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)), тогда метод Committed выполняет срабатывает, но для каждого отдельного вызова SaveChanges() (ожидается, поскольку EF выполняет транзакции самостоятельно).

Я что-то упускаю? Я не могу найти много подробностей о TransactionScopes + IDbTransactionInterceptor. Перехватчик не работает с областями действия и только с фактическими транзакциями EF (например, context.Database.BeginTransaction)?

Код:

TransactionScopeFactory:

public static TransactionScope GetAsyncTransactionScope()
{
    return new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled);
}

Контроллер WebApi:

public async Threading.Task<SaveNewMessageResponse> SaveNewMessage(SaveNewMessageRequest request)
{
    SaveNewMessageResponse response;
    using (TransactionScope tx = TransactionScopeFactory.GetAsyncTransactionScope())
    {
        using (var messageBA = IoCContainer.Resolve<IMessageBusinessAccess>())
        {
            response = await messageBA.SaveNewMessage(request);
        }
        tx.Complete();
    }
    return response;
}

Бизнес-класс:

public async Threading.Task<SaveNewMessageResponse> SaveNewMessage(SaveNewMessageRequest request)
{
    // some async/await stuff, eventually a call to SaveChanges()
}

Перехватчик:

public void Committed(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext)
{
    foreach (var entry in interceptionContext.DbContexts.First().ChangeTracker.Entries())
    {
        var entity = entry.Entity;
        var changeType = entry.State;

        using (var queue = IoCContainer.Resolve<IQueueController>())
        {
            var eventName = entry.GetType().Name + changeType;
            queue.Publish(eventName, entity);
        }
    }
}

person Joe Pontani    schedule 21.07.2017    source источник


Ответы (1)


Я знаю, что этот вопрос немного устарел, но я отвечу всем, кто придет к нему в поисках ответов, как и я.

Похоже, вы уже догадались об ответе:

Я что-то упускаю? Я не могу найти много подробностей о TransactionScopes + IDbTransactionInterceptor. Перехватчик не работает с областями и только с фактическими транзакциями EF (например, context.Database.BeginTransaction)?

Я провел несколько экспериментов с IDbTransactionInterceptor. При использовании TransactionScope в System.Transactions.dll перехватчик никогда не будет вызван. Это действительно имеет смысл, поскольку System.Transactions.dll был написан задолго до того, как был добавлен перехват EF.

Если вы переключитесь на использование context.Database.BeginTransaction (), то перехватчик теперь будет работать. Частично, поскольку кажется, что несколько методов никогда не вызываются:

IsolationLevelGetting () IsolationLevelGot ()

person SpaceGhost440    schedule 22.10.2018