可能的连接泄漏导致“System.Data.SqlClient.SqlException:超时已过期” SQL Server 中出现错误?

发布于 2024-09-01 04:07:14 字数 2555 浏览 4 评论 0原文

我的应用程序要求用户登录并允许他们编辑事物列表。但是,似乎如果同一用户总是登录和注销并编辑列表,则该用户将遇到“System.Data.SqlClient.SqlException:超时已过期”。错误。我读过有关它的评论,可能是由未提交的事务引起的。我确实有一个正在申请中。

我将提供我正在使用的代码,其中有一个 IF 语句,我对此有点犹豫,但这似乎是一个合理的事情。

我将回顾一下这里发生的情况,有一个要更新或添加到数据库中的对象列表。应用程序中创建的新对象的 ID 为 0,而现有对象则拥有从数据库生成的自己的 ID。如果用户选择删除某些对象,它们的 ID 将存储在单独的整数列表中。一旦用户准备好保存更改,两个列表就会传递到此方法中。通过使用 IF 语句,添加 ID 为 0 的对象(使用 Add 存储过程),并更新那些 ID 非零的对象(使用 Update 存储过程)。之后,FOR 循环将遍历“删除”列表中的所有整数,并使用删除存储过程来删除它们。所有这一切都使用事务。

Public Shared Sub UpdateSomethings(ByVal SomethingList As List(Of Something), ByVal RemovalList As List(Of Integer))
Using DBConnection As New SqlConnection(conn)
    DBConnection.Open()
    Dim MyTransaction As SqlTransaction
    MyTransaction = DBConnection.BeginTransaction()
    Try
        Using MyCommand As New SqlCommand()
            MyCommand.Transaction = MyTransaction
            MyCommand.CommandType = CommandType.StoredProcedure

            For Each SomethingItem As Something In SomethingList
                MyCommand.Connection = DBConnection
                If SomethingItem.ID > 0 Then
                    MyCommand.CommandText = "UpdateSomething"
                Else
                    MyCommand.CommandText = "AddSomething"
                End If
                MyCommand.Parameters.Clear()
                With MyCommand.Parameters
                    If MyCommand.CommandText = "UpdateSomething" Then
                        .Add("@id", SqlDbType.Int).Value = SomethingItem.ID
                    End If
                    .Add("@stuff", SqlDbType.Varchar).Value = SomethingItem.Stuff
                End With
                MyCommand.ExecuteNonQuery()
            Next

            MyCommand.CommandText = "DeleteSomething"
            For Each ID As Integer In RemovalList
                MyCommand.Parameters.Clear()
                With MyCommand.Parameters
                    .Add("@id", SqlDbType.Int).Value = ID
                End With
                MyCommand.ExecuteNonQuery()

            Next
        End Using
        MyTransaction.Commit()
    Catch ex As Exception
        MyTransaction.Rollback()
        'Exception handling goes here   '
    End Try

End Using
End Sub

这里使用了三个存储过程以及一些循环,因此我可以看到如果列表足够大,某些东西如何能够支撑一切。

我使用 Visual Studio 2008 进行调试,并使用 SQL Server 2000 作为数据库。

编辑:我似乎仍然收到此错误。我什至已经删除了整个交易的事情,但我仍然遇到它。此时,我假设这里发生了某种泄漏。我尝试不使用 USING 语句并明确告诉命令和连接自行处置,但没有骰子。如果短时间内多次调用此方法,SQL Server 的内存使用量也会增加很多。

我读到增加 SQLCommand 的 CommandTimeout 属性会有所帮助。我想知道这样做是否有什么大的缺点或后果。

My application requires a user to log in and allows them to edit a list of things. However, it seems that if the same user always logs in and out and edits the list, this user will run into a "System.Data.SqlClient.SqlException: Timeout expired." error. I've read a comment about it possibly caused by uncommitted transactions. And I do have one going in the application.

I'll provide the code I'm working with and there is an IF statement in there that I was a little iffy about but it seemed like a reasonable thing to do.

I'll just go over what's going on here, there is a list of objects to update or add into the database. New objects created in the application are given an ID of 0 while existing objects have their own ID's generated from the DB. If the user chooses to delete some objects, their IDs are stored in a separate list of Integers. Once the user is ready to save their changes, the two lists are passed into this method. By use of the IF statement, objects with ID of 0 are added (using the Add stored procedure) and those objects with non-zero IDs are updated (using the Update stored procedure). After all this, a FOR loop goes through all the integers in the "removal" list and uses the Delete stored procedure to remove them. A transaction is used for all this.

Public Shared Sub UpdateSomethings(ByVal SomethingList As List(Of Something), ByVal RemovalList As List(Of Integer))
Using DBConnection As New SqlConnection(conn)
    DBConnection.Open()
    Dim MyTransaction As SqlTransaction
    MyTransaction = DBConnection.BeginTransaction()
    Try
        Using MyCommand As New SqlCommand()
            MyCommand.Transaction = MyTransaction
            MyCommand.CommandType = CommandType.StoredProcedure

            For Each SomethingItem As Something In SomethingList
                MyCommand.Connection = DBConnection
                If SomethingItem.ID > 0 Then
                    MyCommand.CommandText = "UpdateSomething"
                Else
                    MyCommand.CommandText = "AddSomething"
                End If
                MyCommand.Parameters.Clear()
                With MyCommand.Parameters
                    If MyCommand.CommandText = "UpdateSomething" Then
                        .Add("@id", SqlDbType.Int).Value = SomethingItem.ID
                    End If
                    .Add("@stuff", SqlDbType.Varchar).Value = SomethingItem.Stuff
                End With
                MyCommand.ExecuteNonQuery()
            Next

            MyCommand.CommandText = "DeleteSomething"
            For Each ID As Integer In RemovalList
                MyCommand.Parameters.Clear()
                With MyCommand.Parameters
                    .Add("@id", SqlDbType.Int).Value = ID
                End With
                MyCommand.ExecuteNonQuery()

            Next
        End Using
        MyTransaction.Commit()
    Catch ex As Exception
        MyTransaction.Rollback()
        'Exception handling goes here   '
    End Try

End Using
End Sub

There are three stored procedures used here as well as some looping so I can see how something can be holding everything up if the list is large enough.

I'm using Visual Studio 2008 to debug and am using SQL Server 2000 for the DB.

Edit: I still seem to be getting this error. I've even removed the whole transaction thing and I still encounter it. At this point, I'm assuming there is some kind of leak happening here. I've tried not using the USING statements and explicitly tell the command and connection to dispose itself but no dice. Memory usage by SQL Server also increases quite a bit if this method is called a lot in a short period of time.

I've read that increasing the CommandTimeout property of the SQLCommand would help. I'm wondering if there are any big disadvantages or consequences from doing so.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

微凉 2024-09-08 04:07:15

如果您有大量命令,您可能需要在打开连接之前构建所有命令。启动事务并打开连接后,旋转并执行它们。

您可能希望使用 TransactionScope

Using _tx as New System.Transactions.TransactionScope(<add your own timeout here>)

 'Do all your sql work' 

 If _noErrors Then
  _tx.Complete()
 End If

End Using 

与事务范围,您可以设置最多 20 分钟的超时,而无需修改服务器设置。

If you have a large number of commands, you may want to build them all before opening the connection. After you start the transaction and open the connection, spin through and execute them.

You probably want to use TransactionScope

Using _tx as New System.Transactions.TransactionScope(<add your own timeout here>)

 'Do all your sql work' 

 If _noErrors Then
  _tx.Complete()
 End If

End Using 

With the transaction scope, you can set a timeout of up to 20 minutes without modifying server settings.

请叫√我孤独 2024-09-08 04:07:15

我相信我已经成功解决了这个问题。我修改了应用程序,以便不会对数据库进行不必要的调用(即,未更改的对象不需要再次更新),并增加了 SQLCommand 对象的 CommandTimeout 属性。到目前为止,没有问题。

也非常感谢您的建议。

I believe I have managed to solve the problem. I have modified the application so that unnecessary calls to the database are not made (i.e. unchanged objects do not need to be updated again) and increased the CommandTimeout property for the SQLCommand object. So far, no problems.

Big thanks for suggestions too.

兲鉂ぱ嘚淚 2024-09-08 04:07:14

我建议使用以下内容,这样 Dispose 将始终被调用并在每个未提交的情况下回滚。

using (SqlConnection sqlCn = new SqlConnection())
{
   using (SqlTransaction myTrans = sqlCn.BeginTransaction())
   {
   ...
   myTrans.Commit();
   }
}

另外,我不认为您需要为每次执行创建一个新的 SqlCommand。只需保持相同并更新 CommandText 和参数即可。

I would suggest using the following, that way Dispose will always be called and be Rolledback in every non-committed case.

using (SqlConnection sqlCn = new SqlConnection())
{
   using (SqlTransaction myTrans = sqlCn.BeginTransaction())
   {
   ...
   myTrans.Commit();
   }
}

Also, I don't believe you need to make a new SqlCommand for every execution. Just maintain the same one and update the CommandText and Parameters.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文