IDbTransactionInterceptor不拦截所需TransactionScope中的事务提交

时间:2017-07-21 16:37:44

标签: c# entity-framework transactions entity-framework-6 interceptor

上下文:我尝试将数据库事件和数据发布到队列,但前提是数据/事务已成功提交。我们这样做,以便我们可以在此数据上运行后续逻辑(后处理)。我们已将事件格式化为[objecttype][action](例如:userAddeduserDeleted等)。我不想触发对SaveChanges()的来电,因为有可能会有多次调用SaveChanges()(包裹在TransactionScope内)相应的帖子处理逻辑可能需要稍后调用SaveChanges()的数据。我不想处理竞争条件,所以我想等待事务提交并将事件推送到队列中。

尝试解决方案:我创建了IDbTransactionInterceptor的实现,并在启动时将其添加到我们的拦截器中。我用逻辑实现了void Committed(DbTransaction transaction, DbTransactionInterceptionContext interceptionContext)方法(其余方法只是空的)。

问题:拦截器中的Committed方法永远不会被命中。事务正常运行(如果出现错误,则DB中没有任何内容)。 TransactionScope设置为new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)

如果我将范围切换为Suppressed(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);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

我知道这个问题有些陈旧,但是我会回答任何寻求像我一样的答案的人。

您似乎已经猜到了答案:

  

我想念什么吗?我找不到太多关于TransactionScopes + IDbTransactionInterceptor的详细信息。拦截器是否仅适用于范围和实际的EF事务(例如context.Database.BeginTransaction)?

我使用IDbTransactionInterceptor进行了一些实验。在System.Transactions.dll中使用TransactionScope时,将永远不会调用拦截器。这实际上是有道理的,因为System.Transactions.dll是在添加EF拦截之前很久才编写的。

如果切换到使用context.Database.BeginTransaction(),则拦截器现在将起作用。部分原因是,似乎从未调用过几种方法:

IsolationLevelGetting() IsolationLevelGot()