C# 对System.Transactions.TransactionScope的Npgsql 4.x支持不起作用?
我们有一个基于web的多层电子健康系统平台,它使用类/对象层次结构。例如,“患者门诊就诊”对象可能包括多个诊断对象、多个药物对象、多个观察对象、实验室测试对象等。使用.NET System.transactions.TransactionScope处理数据库事务 将数据持久化到数据库中时,层次结构中的每个嵌套对象:C# 对System.Transactions.TransactionScope的Npgsql 4.x支持不起作用?,c#,postgresql,transactions,npgsql,C#,Postgresql,Transactions,Npgsql,我们有一个基于web的多层电子健康系统平台,它使用类/对象层次结构。例如,“患者门诊就诊”对象可能包括多个诊断对象、多个药物对象、多个观察对象、实验室测试对象等。使用.NET System.transactions.TransactionScope处理数据库事务 将数据持久化到数据库中时,层次结构中的每个嵌套对象: 实例化TransactionScope(使用默认事务 必需选项) 实例化NpgsqlConnection连接 有自己的SQL和 如果一切顺利,则设置TransactionScope.
void RootMethod()
{
using(TransactionScope scope = new TransactionScope())
{
/* Perform transactional work here */
SomeMethod();
AnotherMethod();
scope.Complete();
}
}
void SomeMethod()
{
using(TransactionScope scope = new TransactionScope())
{
using(NpgsqlConnection connection = new NpgsqlConnection(connectionString))
{
connection.Open()
/* Perform transactional work here */
scope.Complete();
}
}
}
void AnotherMethod()
{
using(TransactionScope scope = new TransactionScope())
{
using(NpgsqlConnection connection = new NpgsqlConnection(connectionString))
{
connection.Open()
/* Perform transactional work here */
scope.Complete();
}
}
}
封装在“using(TransactionScope scope=new TransactionScope()){…}”代码块中的程序代码在Oracle(使用db Oracle驱动程序)上被登记在相同的事务中,但在Postgres上使用Npgsql时,将生成单独的事务,而不是一个事务
因此,Postgres上的事务会因外键约束而失败—数据无法持久化到子表中,因为创建的单独事务在父对象提交数据之前没有看到插入父表(在单独事务中)的数据
我们在Npgsql connectString中得到了inclist=true,并且在我们的Postgres服务器上,服务器参数max_prepared_transactions设置为大于0的值
我们测试的Npgsql驱动程序版本是4.0.7和4.1.2。我们的devt环境中的Postgres服务器版本是版本10
Npgsql文档说System.Transactions.TransactionScope是受支持的(并且从v3.3开始就受到支持),我们在StackOverflow上搜索的其他Npgsql相关答案也是如此
乍一看,在事务性单元测试中似乎只使用一个数据库连接
问题:
- 根据TransactionScope实施指南(例如),Npgsql是否支持多个TransactionScope和多个参与数据库连接
- 有什么明显的遗漏吗
如上所述,使用Npgsql 4.x,在“使用(TransactionScope=new TransactionScope()){…}”中打开每个数据库连接代码块似乎会生成一个新事务,而不是登记在同一个事务中。在内部事务范围中打开第二个NpgsqlConnection之前,需要关闭在外部事务范围中打开的NpgsqlConnection(允许Npgsql在内部重用相同的物理连接,而不会升级到分布式事务)。在内部事务范围中打开第二个NpgsqlConnection之前,需要关闭在外部事务范围中打开的NpgsqlConnection(允许Npgsql在内部重用相同的物理连接,而不会升级到分布式事务).使用最新的Npgsql版本运行上面提供的代码只会触发一个事务,如PG日志所示;SomeMethod和AnotherMethod中的嵌套作用域实际上并不创建事务-它们只是在RootMethod中加入根作用域,它们对完成的调用只是对提交的投票。请注意,您不需要要将max_prepared_transactions设置为非零:上面的代码示例应在不升级为分布式事务的情况下工作。这是因为在(根目录)中只有一个打开的连接事务作用域在给定的时间点,因此Npgsql在内部重用相同的物理连接。如果同时打开两个连接,则会升级到分布式事务(然后我们就处于完全不同的世界中)。请尝试重新检查您认为实际上正在创建多个数据库事务的原因。谢谢Shay,这非常有帮助-为我们指出了代码中的问题。我上面的简化代码说明不太准确(我的错误):看来我们确实在根TransactionScope中打开了一个NpgsqlConnection,但在内部TransactionScope打开第二个NpgsqlConnection之前,我们没有关闭该连接-实际上导致了一个分布式(两个单独的)事务,这是无意的。因此,在同一个数据库上(在Oracle上),单独的程序代码块看不到彼此工作.@shay Rojansky运行您在上面提供的最新Npgsql版本的代码只会触发一个事务,如PG日志所示;,您在SomeMethod和AnotherMethod中的嵌套作用域实际上并不创建事务-它们只是在RootMethod中加入根作用域,它们对Complete的调用只是对commit的投票ld不需要将max_prepared_transactions设置为非零:上面的代码示例应在不升级为分布式事务的情况下工作。这是因为您在(根目录)中只有一个打开的连接事务作用域在给定的时间点,因此Npgsql在内部重用相同的物理连接。如果同时打开两个连接,则会升级到分布式事务(然后我们就处于完全不同的世界中)。请尝试重新检查您认为实际上正在创建多个数据库事务的原因。谢谢Shay,这非常有帮助-为我们指出了代码中的问题。我上面的简化代码说明不太准确(我的错误):看来我们确实在根TransactionScope中打开了一个NpgsqlConnection,但在内部TransactionScope打开第二个NpgsqlConnection之前,我们没有关闭该连接-实际上导致了一个分布式(两个单独的)事务,这是无意的。因此,在同一个数据库上(在Oracle上),单独的程序代码块看不到彼此工作。@shay rojansky