跨多个连接的共享事务,或PostgreSQL中的ReadUncommitted

时间:2014-08-14 22:38:12

标签: c# sql-server postgresql transactions

我想在单个事务范围内打开多个连接,以便每个连接都可以看到先前连接所做的更改。

我需要这个用于测试 - 实际代码写入数据库,测试代码验证数据是否实际插入/更新。最后,我回滚了事务范围,以便真正的数据库不受影响。

这种方法在SQL Server中运行良好,但在PostgreSQL中似乎不起作用(我使用9.3与Npgsql提供程序),下面是一个小例子。


这是在事务范围内运行任意查询的帮助程序

private void RunQuery(string query, Action<IDataReader> process)
{
    using (var connection = new NpgsqlConnection(Config.ConnectionString)) {
        connection.Open();
        connection.EnlistTransaction(Transaction.Current);

        using (var command = connection.CreateCommand()) {
            command.CommandText = query;
            using (var reader = command.ExecuteReader()) {
                while (reader.Read()) {
                    process(reader);
                }
            }
        }
    }
}

..这里是测试代码 - 它插入users表,然后检查用户是否实际插入:

using (var scope = new TransactionScope()) {

    //"tested scenario"
    int id = 0;
    RunQuery("INSERT INTO users (name) VALUES ('foo') RETURNING id;", reader => {
        id = (int)reader.GetValue(0);
    });

    //checking
    int id2 = 0;
    RunQuery("SELECT id, name FROM users WHERE id=" + id, reader => {
        id2 = (int)reader.GetValue(0);
    });    
    Assert.That(id2, Is.Not.EqualTo(0));
}

上述测试在Postgres上失败,因为id2始终为零。我使用TransactionScope尝试了TransactionOptions.ReadUncommitted构造函数,但似乎没有帮助。请注意,如果我针对SQL Server运行此操作(将NpgsqlConnection更改为SqlConection,请使用SCOPE_IDENTITY检索ID)然后一切正常,id2不为零。< / p>

正如您所料,在Postgres的同一连接工作中选择,但我不需要,我的目标是在共享事务范围上使用多个连接。我也不需要多线程,这些连接按顺序发生。

1 个答案:

答案 0 :(得分:7)

首先是免责声明:虽然我对postgresql有点了解,但我对.NET知之甚少。

我怀疑您正在混淆两个相关但又独立的概念 - 分布式事务的概念和存在的事务隔离级别。

根据.NET Documentation,EnlistTransaction将连接添加到分布式事务中。分布式事务是described as follows

  

分布式事务是影响多个事务的事务   资源。对于要提交的分布式事务,所有参与者   必须保证对数据的任何更改都是永久性的。变化必须   尽管系统崩溃或其他不可预见的事件仍然存在。如果连一个   单个参与者未能做出这个保证,整个   事务失败,以及范围内的任何数据更改   交易被回滚。

在数据库中,此类事务通过两阶段提交过程在数据库中实际上独立的事务中实现。通过执行PREPARE TRANSACTION,所有参与的交易都进展到第一阶段的结束。一旦他们都处于这种状态,那么他们就可以通过执行COMMIT PREPARED来完全投入。如果其中任何一个在PREPARE TRANSACTION期间失败,那么它们都可以由ROLLBACK PREPARED回滚。这可以保证它们全部被提交,或者它们都被回滚。

当使用.NET提供的中间件时,您没有看到任何这些细节:框架为您处理两阶段提交。

因此,您可能想知道这与您未看到此分布式事务的一部分中所做的更改在另一部分中反映的事实有什么关系。答案可能一无所获。这两个事务实际上是完全分开的 - 实际上它们可能位于完全独立的数据库中。

您要实现的目标 - 能够在提交之前看到一个事务中的更改在另一个事务中发生 - 与transaction isolation的级别相关。

对你而言,坏消息是,听起来你希望拥有的隔离级别是“未提交的”,而postgresql不支持这种隔离级别。

也许你需要在更高的层次上描述你想要实现的目标 - 很可能还有另一种(可能更好的)方法来实现它。