我正在使用Entity Framework Core和PostgreSQL。我想在我的DbContext中覆盖SaveChanges来提交EF的变化,并在同一个数据库事务中用Rebus发送消息。我的计划是使用PostgreSQL进行传输等,但我在使用下面的代码在事务范围内招募Rebus时遇到了麻烦。
public override int SaveChanges()
{
using (var tx = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
tx.EnlistRebus();
var result = base.SaveChanges();
//TODO: _bus.Send("something happened");
tx.Complete();
return result;
}
}
运行上述代码的结果是 PostgresException: 55000: prepared transactions are disabled
这是对的,因为我的PostgreSQL配置没有启用它。我的问题是,为什么Rebus需要在这里引起一个2阶段提交。也许我错过了什么,尽管我希望不需要2PC,因为Entity Framework和Rebus将使用相同的关系数据库实例。
调用 EnlistRebus
是在Rebus.TransactionScopes包中,不知道使用的是什么传输实现,可能会做安全的事情。
有没有其他方法可以同时进行数据库操作和Rebus事务性发送而不需要2阶段提交?当然,我可以用一个单独的表来存储我的待处理消息,从 SaveChanges
并让一个单独的工作者从该表中提取并使用Rebus发送消息。我怀疑这种方法是最稳妥和直接的。
我使用的是Rebus 6.3.0、Rebus.PostgreAql 7.1.0、Rebus.TransactionScopes 6.0.0、Npgsql 4.1.3.1、Npgsql.EntityframeworkCore.PostgreSQL 3.1.4和EF Core 3.1.4。
Rebus.PostgreSQL在没有Rebus.TransactionScopes的情况下,实际上会自己加入到环境事务中。
在Rebus.PostgreSQL repo中,我看到有一个PR似乎可以解决我的问题。引用PR的内容 https:/github.comrebus-orgRebus.PostgreSqlpull14。:
我先试着去找 https:/github.comrebus-orgRebus.TransactionScopes。 来工作,但是使用postgresql传输似乎没有任何作用。
为了验证它确实在环境事务中做了消息发布,我做了如下修改。
public override int SaveChanges()
{
using (var tx = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted },
TransactionScopeAsyncFlowOption.Enabled))
{
var result = base.SaveChanges();
_bus.Send(new MyMessage { CurrentDateTime = DateTime.Now}).Wait();
if (ShouldCrash)
{
throw new ArgumentException();
}
tx.Complete();
return result;
}
}
我做了以下修改: ShouldCrash
设置为 true
,不发布消息,也不对实体进行更改。如果 ShouldCrash
设置为 false
消息发布和实体变更都会被执行。
我认为这是因为Npgsql文档中的以下内容。
请注意,如果你在一个环境事务中打开和关闭同一个数据库的连接,而没有同时打开两个连接,Npgsql会在内部重用同一个连接,避免升级到一个完整的分布式事务。