Возможная утечка соединения, вызывающая ошибку «System.Data.SqlClient.SqlException: Timeout expired» в SQL Server? - PullRequest
0 голосов
/ 10 мая 2010

Мое приложение требует от пользователя входа в систему и позволяет ему редактировать список вещей. Однако, похоже, что если один и тот же пользователь всегда входит в систему и редактирует список, он столкнется с «System.Data.SqlClient.SqlException: Timeout expired». ошибка. Я прочитал комментарий об этом, возможно, вызванный незафиксированными транзакциями. И у меня есть один входящий в приложение.

Я предоставлю код, с которым я работаю, и там есть оператор IF, в котором я немного сомневался, но это казалось разумным.

Я просто расскажу, что здесь происходит, есть список объектов, которые нужно обновить или добавить в базу данных. Новым объектам, созданным в приложении, присваивается идентификатор 0, в то время как существующие объекты имеют свои собственные идентификаторы, генерируемые из БД. Если пользователь решает удалить некоторые объекты, их идентификаторы хранятся в отдельном списке целых чисел. Как только пользователь готов сохранить свои изменения, два списка передаются в этот метод. С помощью оператора IF добавляются объекты с идентификатором 0 (с использованием хранимой процедуры Add), а объекты с ненулевыми идентификаторами обновляются (с использованием хранимой процедуры Update). После всего этого цикл FOR проходит через все целые числа в списке «удаления» и использует хранимую процедуру Delete для их удаления. Для всего этого используется транзакция.

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 также значительно возрастает, если этот метод часто вызывается за короткий промежуток времени.

Я читал, что увеличение свойства CommandTimeout команды SQLCommand поможет. Мне интересно, есть ли какие-либо большие недостатки или последствия от этого.

Ответы [ 3 ]

1 голос
/ 10 мая 2010

Если у вас большое количество команд, вы можете собрать их все до открытия соединения. После запуска транзакции и открытия соединения прокрутите и выполните их.

Вы, вероятно, хотите использовать 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 минут без изменения настроек сервера.

1 голос
/ 21 мая 2010

Полагаю, мне удалось решить проблему. Я изменил приложение так, чтобы ненужные вызовы к базе данных не делались (то есть неизмененные объекты не нуждались в повторном обновлении), и увеличил свойство CommandTimeout для объекта SQLCommand. Пока проблем нет.

Большое спасибо за предложения.

1 голос
/ 10 мая 2010

Я бы предложил использовать следующее, чтобы Dispose всегда вызывался и был откатом в каждом необязательном случае.

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

Кроме того, я не верю, что вам нужно создавать новый SqlCommand для каждого выполнения. Просто сохраните тот же самый и обновите CommandText и Parameters.

...