【问题标题】:Use TransactionScope with OracleManagedDataAccess throwing System.PlatformNotSupportedException: 'Operation is not supported on this platform.'将 TransactionScope 与 OracleManagedDataAccess 一起使用会引发 System.PlatformNotSupportedException:“此平台不支持操作。”
【发布时间】:2019-10-30 10:29:35
【问题描述】:

我可以通过尝试在同一个 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

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

在 OracleInternal.MTS.MTSRMManager.CCPEnlistDistributedTxnToSysTxn(OracleConnectionImpl connImpl, Transaction txn, MTSTxnRM txnRM, MTSTxnBranch txnBranch)
在 OracleInternal.MTS.MTSRMManager.CCPEnlistTransaction(OracleConnectionImpl connImpl,事务事务,MTSTxnRM txnRM,MTSTxnBranch txnBranch)
在 OracleInternal.ConnectionPool.PoolManager`3.GetEnlisted(ConnectionString csWithDiffOrNewPwd, Boolean bGetForApp, OracleConnection connRefForCriteria)
在 OracleInternal.ConnectionPool.OracleConnectionDispenser`3.Get(ConnectionString cs、PM conPM、ConnectionString pmCS、SecureString securePassword、SecureString secureProxyPassword、OracleConnection connRefForCriteria)
在 Oracle.ManagedDataAccess.Client.OracleConnection.Open()

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

【问题讨论】:

    标签: c# oracle transactions transactionscope oracle-manageddataaccess


    【解决方案1】:

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

    从 2.1 版开始,.NET Core 中的 System.Transactions 实现不包括对分布式事务的支持,因此您不能使用 TransactionScope 或 CommittableTransaction 来协调多个资源管理器之间的事务。

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

    不支持分布式事务的理由是它们依赖于 Windows 特有的MSDTC。它与 .NET Core 作为跨平台框架的路线图不太一致。

    编辑:另一种方法

    一般而言,在决定依赖分布式事务时应格外小心,因为这些事务很容易成为可扩展性瓶颈。在现代架构中,通常不鼓励分布式事务。

    在您的情况下,由于您的所有资源都是与同一个数据库的连接,因此您实际上并不需要分布式事务。我建议实施(环境)Unit of Work

    例如,您的应用程序中的每个逻辑/业务操作可能都有一个工作单元实例。工作单元实例将缓冲所有请求的对数据库的修改(使用您自己的数据结构来表示修改)。在操作结束时,将提交工作单元。提交操作将包括:

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

    作为另一个例子,Entity Framework Core 在DBContext 类中实现了工作单元模式。

    【讨论】:

    • 那么你的意思是我现在唯一需要确保的是通过我的所有存储库共享一个连接吗?好吧,这似乎并不容易,在某些情况下我们需要某种异步加载,一个相同的连接将无法正常工作。无论如何,这个答案确实指出了 .NET 核心的局限性。
    • 另外,如果我可以通过我的所有存储库共享一个连接,我将只为所有连接共享一个DbTransaction,而不需要使用TransactionScope。如果我们必须确保一个连接,那么TransactionScope 的好处就没有了,在这种情况下真的不值得使用。
    • 你是对的@Hopeless。我扩展了我的答案来解决你的 cmets。
    • 是的,工作单元当然是所有这些涉及事务范围的背后。我知道,但据我所知 TransactionScope 不仅处理分布式事务,还处理本地事务。不知道为什么它没有实现足够智能,例如自动连接所有连接(在同一范围内)以使用一个 DbTransaction 以便所有连接都将一起提交/回滚。如果我自己实现该逻辑,我将需要花费大量时间来设计/测试所有内容。
    • OK :) 您可以使用 AsyncLocal<T> 在多个存储库之间共享环境工作单元。
    猜你喜欢
    • 1970-01-01
    • 2019-12-29
    • 1970-01-01
    • 2022-01-03
    • 1970-01-01
    • 2017-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多