首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >连接池被嵌套的ADO.NET事务损坏(使用MSDTC)

连接池被嵌套的ADO.NET事务损坏(使用MSDTC)
EN

Stack Overflow用户
提问于 2014-05-20 18:00:36
回答 1查看 3K关注 0票数 19

我到处都找不到答案。

我将展示简单的代码片段,这些代码片段展示了如何轻松破坏连接池。

连接池损坏意味着每个新的连接打开尝试都将失败。

体验我们需要的问题的

分布式transaction

  • nested中的事务sqlconnection及其在其他sqlconnection和sqltransaction

  • 中的sqltransaction执行回滚(显式或隐式-简单地不提交)嵌套连接

当连接池损坏时,每个sqlConnection.Open()都会抛出下面的一个:

不允许启动

  • SqlException:新请求,因为它应该与有效的transaction descriptor.
  • SqlException: Distributed transaction一起完成。在新事务或空transaction.

中登记此会话

在ADO.NET中有一些线程竞争,如果我把Thread.Sleep(10)放在代码中的某个地方,它可能会把收到的异常改为第二个异常。有时它会在没有任何修改的情况下改变。

如何重现

通过default).

  • Create empty console application.

  • Create 2

  • (可以为空)或1
  1. 并取消注释行启用分布式事务处理协调器windows服务:Transaction.Current.EnlistDurable[...]
  2. Copy&paste以下代码:

代码语言:javascript
复制
var connectionStringA = String.Format(@"Data Source={0};Initial Catalog={1};Integrated Security=True;pooling=true;Max Pool Size=20;Enlist=true",
            @".\YourServer", "DataBaseA");
var connectionStringB = String.Format(@"Data Source={0};Initial Catalog={1};Integrated Security=True;pooling=true;Max Pool Size=20;Enlist=true",
            @".\YourServer", "DataBaseB");

try
{
    using (var transactionScope = new TransactionScope())
    {
        //we need to force promotion to distributed transaction:
        using (var sqlConnection = new SqlConnection(connectionStringA))
        {
            sqlConnection.Open();
        }
        // you can replace last 3 lines with: (the result will be the same)
        // Transaction.Current.EnlistDurable(Guid.NewGuid(), new EmptyIEnlistmentNotificationImplementation(), EnlistmentOptions.EnlistDuringPrepareRequired);

        bool errorOccured;
        using (var sqlConnection2 = new SqlConnection(connectionStringB))
        {
            sqlConnection2.Open();
            using (var sqlTransaction2 = sqlConnection2.BeginTransaction())
            {
                using (var sqlConnection3 = new SqlConnection(connectionStringB))
                {
                    sqlConnection3.Open();
                    using (var sqlTransaction3 = sqlConnection3.BeginTransaction())
                    {
                        errorOccured = true;
                        sqlTransaction3.Rollback();
                    }
                }
                if (!errorOccured)
                {
                    sqlTransaction2.Commit();
                }
                else
                {
                    //do nothing, sqlTransaction3 is alread rolled back by sqlTransaction2
                }
            }
        }
        if (!errorOccured)
            transactionScope.Complete();
    }
}
catch (Exception e)
{
    Console.WriteLine(e.Message);
}

然后是

代码语言:javascript
复制
for (var i = 0; i < 10; i++) //all tries will fail
{
    try
    {
        using (var sqlConnection1 = new SqlConnection(connectionStringB))
        {
            // Following line will throw: 
            // 1. SqlException: New request is not allowed to start because it should come with valid transaction descriptor.
            // or
            // 2. SqlException: Distributed transaction completed. Either enlist this session in a new transaction or the NULL transaction.
            sqlConnection1.Open();
            Console.WriteLine("Connection successfully open.");
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e.Message);
    }
}

已知的不良解决方案以及可以观察到的有趣之处

差的解决方案:

在嵌套的sqltransaction中使用块do的

使用sqlconnection从外部块包装所有嵌套块( TransactionScopes必须包装SqlConnection.Open())

  • In嵌套块

有趣的观察:

  1. 如果应用程序在连接池损坏后等待几分钟,那么一切工作正常。因此连接池损坏只持续连接了几个minutes.
  2. With调试器。当执行离开外部sqltransaction时,将抛出使用块SqlException: The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.try ... catch .....

不能捕获该异常

如何解决?

这个问题使我的web应用程序几乎死掉(无法打开任何新的sql连接)。

给出的代码片段是从整个管道中提取出来的,这些管道也包含对第三方框架的调用。我不能简单地更改代码。

  • 有没有人知道到底哪里出了问题?
  • Is it ADO.NET bug?
  • 也许我(还有一些框架...)做错了什么?

My environment (它似乎并不是很重要)

  • .NET框架4.5
  • MS SQL .NET 2012
EN

回答 1

Stack Overflow用户

发布于 2015-03-12 01:48:35

我知道这个问题很久以前就有人问过了,但我想我已经为那些仍然有这个问题的人找到了答案。

SQL中的嵌套事务与它们在创建它们的代码结构中所显示的不同。

无论有多少嵌套事务,只有外部事务很重要。

为了使外部事务能够提交,内部事务必须提交,换句话说,如果内部事务提交,则内部事务不起作用-外部事务仍然必须提交才能完成事务。

但是,如果内部事务回滚,则外部事务将回滚到其起始。外部事务必须仍然回滚或提交-否则它在已启动状态中仍然是

因此,在上面的示例中,行

代码语言:javascript
复制
//do nothing, sqlTransaction3 is alread rolled back by sqlTransaction2

应该是

代码语言:javascript
复制
sqlTransaction2.Rollback();

除非有其他事务可以完成,因此也就完成了外部事务。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23756659

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档