将 TransactionScope 与 OracleManagedDataAccess 一起使用抛出 System.PlatformNotSupportedException:'Operation is not supported on this platform.'

Use TransactionScope with OracleManagedDataAccess throwing System.PlatformNotSupportedException: 'Operation is not supported on this platform.'

我可以通过尝试在同一个 TransactionScope 中打开 2 个连接来成功重现此问题(即使第一个连接在打开下一个连接之前已关闭),如下所示:

 var connectionString = "some connection";
 using (var t = new TransactionScope())
 {
    using (var con1 = new OracleConnection(connectionString))
    {
       con1.Open();
    }                    
    using (var con2 = new OracleConnection(connectionString))
    {
       con2.Open();//exception thrown at here
    }
 }  

实际上,我正在尝试利用 TransactionScope 为我的存储库实现某种 Ambient transaction(每个存储库都使用相同的连接字符串打开自己的连接)。上面的代码已尽可能简化以帮助重现异常。

我不确定我是不是做错了什么,或者 TransactionScope 至少在 .NET Standard 中不受 OracleManagedDataAccess 支持。 我的项目以 .NET Standard 2.0 (lib) 和 .NET Core (app) 2.2 为目标,OracleManagedDataAccess 是通过 nuget 安装的(当然是针对 .NET Standard)并且版本为 2.19.3.

这是上面发布的异常的堆栈跟踪:

at OracleInternal.MTS.MTSRMManager.CCPEnlistDistributedTxnToSysTxn(OracleConnectionImpl connImpl, Transaction txn, MTSTxnRM txnRM, MTSTxnBranch txnBranch)
at OracleInternal.MTS.MTSRMManager.CCPEnlistTransaction(OracleConnectionImpl connImpl, Transaction transaction, MTSTxnRM txnRM, MTSTxnBranch txnBranch)
at OracleInternal.ConnectionPool.PoolManager`3.GetEnlisted(ConnectionString csWithDiffOrNewPwd, Boolean bGetForApp, OracleConnection connRefForCriteria)
at OracleInternal.ConnectionPool.OracleConnectionDispenser`3.Get(ConnectionString cs, PM conPM, ConnectionString pmCS, SecureString securedPassword, SecureString securedProxyPassword, OracleConnection connRefForCriteria)
at Oracle.ManagedDataAccess.Client.OracleConnection.Open()

我感觉这是一个非常棘手的问题,几乎取决于OracleManagedDataAccess。如果我不能使用TransactionScope,就没有简单的方法来实现Ambient transaction

TransactionScope 中的单个连接应该按预期工作。 当您向 TransactionScope 申请两个连接时,您正在尝试执行分布式事务,这在 .NET Core 中不受支持:

As of version 2.1, the System.Transactions implementation in .NET Core does not include support for distributed transactions, therefore you cannot use TransactionScope or CommittableTransaction to coordinate transactions across multiple resource managers.

(来自 Microsoft Docs 关于 EF Core 的文章 Using Transactions)。

不支持分布式事务的理由是它们依赖 MSDTC,这是 Windows 特有的。它与成为 cross-paltform 框架的 .NET Core 路线图不太一致。

编辑:另一种方法

一般来说,依赖分布式事务的决定应该格外小心,因为这些事务很容易成为可伸缩性瓶颈。在现代架构中,通常不鼓励分布式事务。

在你的例子中,因为你所有的资源都连接到同一个数据库,所以你实际上不需要分布式事务。我建议实施(环境)Unit of Work.

例如,您的应用程序中每个 logical/business 操作可能有一个 unit-of-work 实例。 unit-of-work 的实例将缓冲所有请求的对数据库的修改(使用您自己的数据结构来表示修改)。在操作结束时,unit-of-work 将被提交。提交操作将包括:

  • 正在打开数据库连接和数据库事务
  • 使用单个数据库连接执行所有缓冲修改
  • 正在提交数据库事务并关闭数据库连接

再举一个例子,Entity Framework Core 在 DBContext class 中实现了工作单元模式。