上下文:我尝试将数据库事件和数据发布到队列,但前提是数据/事务已成功提交。我们这样做,以便我们可以在此数据上运行后续逻辑(后处理)。我们已将事件格式化为[objecttype][action]
(例如:userAdded
,userDeleted
等)。我不想触发对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);
}
}
}
答案 0 :(得分:0)
我知道这个问题有些陈旧,但是我会回答任何寻求像我一样的答案的人。
您似乎已经猜到了答案:
我想念什么吗?我找不到太多关于TransactionScopes + IDbTransactionInterceptor的详细信息。拦截器是否仅适用于范围和实际的EF事务(例如context.Database.BeginTransaction)?
我使用IDbTransactionInterceptor进行了一些实验。在System.Transactions.dll中使用TransactionScope时,将永远不会调用拦截器。这实际上是有道理的,因为System.Transactions.dll是在添加EF拦截之前很久才编写的。
如果切换到使用context.Database.BeginTransaction(),则拦截器现在将起作用。部分原因是,似乎从未调用过几种方法:
IsolationLevelGetting() IsolationLevelGot()