【发布时间】:2017-07-21 16:37:44
【问题描述】:
上下文:我正在尝试将数据库事件和数据发布到队列,但前提是数据/事务已成功提交。我们这样做是为了对这些数据运行后续逻辑(后处理)。我们已将事件格式化为 [objecttype][action](例如:userAdded、userDeleted 等)。我不想触发对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