“交易状态无效”错误和事务范围。

86

当我尝试调用包含SELECT语句的存储过程时,出现以下错误:

该操作对于事务状态无效。

这是我的调用结构:

public void MyAddUpdateMethod()
{

    using (TransactionScope Scope = new TransactionScope(TransactionScopeOption.RequiresNew))
    {
        using(SQLServer Sql = new SQLServer(this.m_connstring))
        {
            //do my first add update statement

            //do my call to the select statement sp
            bool DoesRecordExist = this.SelectStatementCall(id)
        }
    }
}

public bool SelectStatementCall(System.Guid id)
{
    using(SQLServer Sql = new SQLServer(this.m_connstring)) //breaks on this line
    {
        //create parameters
        //
    }
}

在事务中创建另一个到同一数据库的连接是否会出问题?

11个回答

70

经过一些研究,似乎我不能在TransactionScope块中打开到同一个数据库的两个连接。我需要修改我的代码,使其看起来像这样:

public void MyAddUpdateMethod()
{
    using (TransactionScope Scope = new TransactionScope(TransactionScopeOption.RequiresNew))
    {
        using(SQLServer Sql = new SQLServer(this.m_connstring))
        {
            //do my first add update statement            
        }

        //removed the method call from the first sql server using statement
        bool DoesRecordExist = this.SelectStatementCall(id)
    }
}

public bool SelectStatementCall(System.Guid id)
{
    using(SQLServer Sql = new SQLServer(this.m_connstring))
    {
        //create parameters
    }
}

我遇到了同样的情况。我必须在同一事务范围内引用两个不同的数据库。感谢这个提示。 - rageit
5
好的,一个常见的情况是如果你有一个写入数据库的日志框架(如nlog、log4net),由于日志框架会像你的应用程序一样创建自己的数据库连接,因此很容易出现这种情况。 - viggity
2
记录应用程序可能应该抑制任何外部TransactionScope上下文。 - user2864740
NLog抑制任何外部TransactionScope:https://github.com/NLog/NLog/wiki/Database-target - Alex
我在同一个TransactionScope下使用了两个using语句打开了相同的连接,但仍然可以正常工作。我使用的是SqlConnection而不是你上面示例中使用的SQLServer。 - Rajaram Shelar

18

当我遇到这个异常时,有一个内部异常"事务超时"。由于这是在调试会话期间发生的,在TransactionScope内停留了一段时间后,我选择忽略这个问题。

当这个特定的带有超时的异常出现在部署的代码中时,我认为以下部分在您的 .config 文件中将会对您有所帮助:

<system.transactions> 
        <machineSettings maxTimeout="00:05:00" /> 
</system.transactions>

1
TransactionOptions.Timeout 相同吗? - Kiquenet
请注意,当机器范围内的.NET框架配置超过10分钟时,它将覆盖此设置! - DdW

14

我也遇到了同样的问题,我将事务超时时间改为15分钟,然后问题得到了解决。 希望这能有所帮助。

TransactionOptions options = new TransactionOptions();
options.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
options.Timeout = new TimeSpan(0, 15, 0);
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required,options))
{
    sp1();
    sp2();
    ...

}

18
我强烈怀疑这不是超时时间改变了行为,而是您将隔离级别从Serializable更改为ReadCommitted的事实。 - Russell Horwood
这个异常也可能在超时的情况下发生,关于超时的信息也应该包含在异常数据中。 - Pawel Maga

8

针对将来遇到此问题的任何游荡者。如果您的应用程序和数据库位于不同的计算机上,尤其是在使用TransactionScope时出现上述错误,请启用网络DTC访问。 执行以下步骤:

  1. 添加防火墙规则以允许计算机互相通信。
  2. 确保分布式事务协调器服务正在运行。
  3. 启用网络dtc访问。运行dcomcnfg。 转到组件服务>我的电脑>分布式事务协调器>本地DTC。右键单击属性。
  4. 按照所示启用网络dtc访问。

重要提示:请勿编辑/更改DTC登录帐户字段中的用户帐户和密码,将其保留原样,否则你最终可能需要重新安装Windows。

DTC photo


@KJN 谢谢!我们应该在安装 SQL Server 的机器上进行吗? - lilo.jacob
1
是的,在 SQL Server 运行的地方执行它。 - J. Minjire
嗯,我在Linux服务器上使用MySQL :/? 有什么建议吗? - Vedran Mandić
嗯,我在Linux服务器上使用MySQL :/? 有什么建议吗? - undefined
1
抱歉 @VedranMandić,我对Linux的了解几乎为零。发一个带有linux、mysql等标签的问题吧,或许能从其他具备linux/mysql知识的人那里得到帮助。 - J. Minjire

5

当我的事务嵌套在另一个事务内部时,我遇到了这个错误。可能存储过程声明了自己的事务,或者调用函数声明了一个事务吗?


我在任何存储过程中都没有任何T-SQL事务代码。理论上,事务应该由MyAddUpdateMethod()方法控制。 - Michael Kniskern
+1 是因为当我存储过程中的事务语言错误时,即事务计数混乱时,我收到了此错误。 - codeMonkey

3

对我而言,在尝试在另一个事务块内遇到异常后回滚事务块时,出现了这个错误。

我所需要做的就是删除我的内部事务块,以解决这个问题。

当使用嵌套事务时,情况可能会变得非常混乱,最好避免这种情况并重构您的代码。


3
在我的情况下,解决方案既不是增加“transactionscope”的时间,也不是增加“system.transactions”的“machineSettings”属性的机器配置文件。
这种情况很奇怪,因为只有在信息量非常大的情况下才会出现此错误。
所以问题的原因是,在事务内部的代码中有许多“foreach”,用于更新不同的表格(我必须在其他人员开发的代码中解决此问题)。如果使用少量记录进行测试,则不会显示错误,但如果增加记录数量,则会显示错误。
最终的解决方案是将单个事务更改为几个独立的事务,这些事务位于不同的“foreach”中。

1
我在调试过程中意外超时了交易,遇到了这个问题。所以其他人可能需要检查一下是否也是这种情况。

0

我更新了一些处理数据库过程的专有第三方库,然后在所有保存调用中遇到了这个错误。我不得不更改web.config事务密钥部分中的一个值,因为(事实证明)所引用的事务范围方法已从一个库移动到另一个库。

之前与该密钥相关的还有其他错误,但我通过注释掉该密钥来摆脱它们(我知道那时应该怀疑,但我假设它现在已经过时了,因为它不在新的库中)。


0

你不能同时打开两个事务。我的做法是在返回将被第二个事务使用的结果之前,指定transaction.Complete() ;)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接