在 TransactionScope 中加入 Rebus 并结合 Entity Framework Core 3.0,无需两阶段提交

Enlist Rebus in a TransactionScope combined with Entity Framework Core 3.0 without 2-phase commit

我正在使用 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 需要在这里进行两阶段提交。也许我遗漏了一些东西,尽管我希望不需要 2PC,因为 Entity Framework 和 Rebus 都将使用相同的关系数据库实例。

EnlistRebus 的调用在 Rebus.TransactionScopes 包中,不知道正在使用什么传输实现,可能会做安全的事情。

有没有另一种方法可以在不进行两阶段提交的情况下同时进行数据库操作和 Rebus 事务发送?我当然可以使用一个单独的 table 来存储来自 SaveChanges 的待处理消息,并让一个单独的工作人员从 table 中拉出并使用 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 回购协议,我看到有一个 PR 似乎可以解决我的问题。来自 https://github.com/rebus-org/Rebus.PostgreSql/pull/14 的 PR 的引述:

I first tried getting https://github.com/rebus-org/Rebus.TransactionScopes to work, but that does not seem to do anything using the postgresql transport.

为了验证它确实在环境事务中发布消息,我做了以下修改:

    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 文档中的以下内容:

Note that if you open and close connections to the same database inside an ambient transaction, without ever having two connections open at the same time, Npgsql will internally reuse the same connection, avoiding the escalation to a full-blown distributed transaction.