【问题标题】:IDbTransactionInterceptor is not intercepting transaction commits within a required TransactionScopeIDbTransactionInterceptor 未在所需的 TransactionScope 内拦截事务提交
【发布时间】:2017-07-21 16:37:44
【问题描述】:

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

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

问题:拦截器中的Committed 方法永远不会被命中。事务正常运行(如果出现错误,则数据库中没有任何内容)。 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);
        }
    }
}

【问题讨论】:

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


    【解决方案1】:

    我知道这个问题有点过时了,但我会为任何像我一样寻求答案的人回答。

    看来你已经猜到答案了:

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

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

    如果你切换到使用 context.Database.BeginTransaction() 那么拦截器现在可以工作了。部分原因是,似乎有几个方法从未被调用:

    IsolationLevelGetting() 隔离级别Got()

    【讨论】:

      猜你喜欢
      • 2019-06-04
      • 1970-01-01
      • 2017-01-08
      • 1970-01-01
      • 1970-01-01
      • 2013-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多