Соединение с SQL Server остается открытым даже после закрытия - PullRequest
0 голосов
/ 21 апреля 2019

У меня есть сайт, который предназначен для многоуровневой. Мой код работает, но я заметил, что чем больше становится мое приложение, тем больше соединений базы данных SQL начинают складываться и остаются открытыми. Это в конечном итоге вызывает эту ошибку:

System.InvalidOperationException: 'Истекло время ожидания. Тайм-аут период истек до получения соединения из пула. это могло произойти, потому что все пул соединений были использованы и макс достигнут размер бассейна. '

Мой код разделен на 3 слоя. Это:

  1. Прикладной уровень. Каждый раз, когда он хочет CRUD, вызывает бизнес-уровень.
  2. Бизнес-уровень - делает бизнес-логику. Когда он хочет взаимодействовать с базой данных MS SQL, он подключается через слой ConnectionAdapter.
  3. ConnectionAdapter наследуется от класса SqlConnectionAdapter и выполняет фактические взаимодействия БД.

Ниже приведен псевдокод для каждого:

Применение
Мое приложение может вызывать бизнес-уровень несколько раз. Особенно при выполнении запросов AJAX. Пример будет выглядеть так:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
    Dim dp As New DataProxy
    Dim listOfObs As New List(Of MyObject)
    dim someId as integer = 1
    Try
        If Not Page.IsPostBack Then
          listOfObs = dp.ExampleReadFuncion(someId)
        End If
    Catch ex As Exception
        Throw
    Finally
        dp.dispose()
        dp = Nothing
        SetMenue()
    End Try
End Sub

DatatProxy (бизнес-уровень)

Public Class DataProxy
    Dim scConAdapter As New ConnectionAdapter

    Public Sub New()
        Try          
            scConAdapter.Connect()        
        Catch ex As Exception
            Throw      
        End Try    
    End Sub    

    Public Sub dispose()
        scConAdapter.Dispose()
    End Sub

    Private Shared Sub Main()
    End Sub

    Public Function ExampleReadFuncion(ByVal someId As Integer) As List(Of MyObject)
        Dim successFactor As LogStatusEnum = LogStatusEnum.INFO
        Dim newEx As Exception = Nothing
        Dim conn As New ConnectionAdapter
        Dim myObj As ActivityMarker
        Dim listOfObs As New List(Of MyObject)
        Dim dr As SqlDataReader = Nothing

        Try
            successFactor = LogStatusEnum.INFO
            conn.Connect()
            dr = conn.ExampleReadFuncion(someId)
            Using dr
                If (dr.HasRows = True) Then
                    While dr.Read
                        myObj = New myObj
                        myObj.Marker_Id = dr.Item("id")
                        myObj.Acitvity_Id = dr.Item("someValue")   

                        listOfObs.Add(myObj)
                    End While
                End If
            End Using
            Return listOfObs
        Catch ex As Exception
            successFactor = LogStatusEnum.ERRORS         
            Throw
        Finally
            dr.Close()
            dr = Nothing
            conn.Dispose()
            conn = Nothing     
        End Try
    End Function

End class

ConnectionAdapter

Public Class ConnectionAdapter
    Inherits SqlConnectionAdapter

    Public Sub New()
    End Sub

    Public Function ExampleReadFuncion(ByVal someId As Integer) As SqlDataReader
        Try
            Dim dr As SqlDataReader = Nothing
            Dim selectString As New StringBuilder
            Dim cmd As SqlCommand = Nothing
            Try
                cmd = CreateCommand()

                selectString.Append("SELECT * " & vbCrLf)
                selectString.Append("FROM " & vbCrLf)
                selectString.Append("dbo.mytable " & vbCrLf)
                selectString.Append("WHERE  " & vbCrLf)
                selectString.Append("id = @SOME_ID " & vbCrLf)              

                With cmd
                    .CommandType = CommandType.Text
                    .CommandText = selectString.ToString
                    .Parameters.Add("@SOME_ID", SqlDbType.Int).Value = someId

                    dr = .ExecuteReader
                End With

            Catch ex As Exception
                Throw
            Finally           
                cmd.Dispose()
            End Try
            Return dr
        Catch ex As Exception
            Throw ex
        End Try
    End Function
end class

1039 * SqlConnectionAdapter *

Public MustInherit Class SqlConnectionAdapter
    Protected CurrentTransaction As SqlTransaction
    Public Property db As SqlConnection
    Public Property Password As String
    Public Property TNSName As String
    Public Property User As String
    Public Property DBName As String
    Public Property PortNumber As Integer   

    Public Overridable Sub Dispose()
        Try
            If Not CurrentTransaction Is Nothing Then
                CurrentTransaction.Commit()
            End If

        Catch ex As Exception
            Throw
        Finally
            If Not db Is Nothing Then
                db.Close()
                db.Dispose()
                db = Nothing
            End If
        End Try
    End Sub

    Public Overridable Sub Connect()
        Try
            Dim appSettings = ConfigurationManager.AppSettings

            If (appSettings("releaseVersion") = "DEBUG") Then
                Connect(appSettings("db_sqlHost"), appSettings("db_sqlDb"))
            Else
                Connect(appSettings("db_sqlHost"), appSettings("db_sqlPort"), appSettings("db_sqlDb"), appSettings("db_sqlUser"), appSettings("db_sqlPassword"))
            End If

        Catch ex As Exception
            Throw
        End Try
    End Sub

    Public Sub Connect(ByVal GetServername As String, ByVal GetDatabaseName As String)
        Try
            TNSName = GetServername
            DBName = GetDatabaseName

            db = New SqlConnection
            db = SqlConnectionUtilities.GetConnection(GetServername, GetDatabaseName)
        Catch ex As Exception
            Throw
        End Try
    End Sub

    Public Sub Connect(ByVal GetServerName As String, ByVal GetPort As Long, ByVal GetDatabase As String, ByVal GetUsername As String, ByVal Getpassword As String)
        Try            
            User = GetUsername
            Password = Getpassword
            PortNumber = GetPort
            DBName = GetDatabase
            TNSName = GetServerName

            db = New SqlConnection
            db = SqlConnectionUtilities.GetConnection(GetServerName, GetPort, GetDatabase, GetUsername, Getpassword)
        Catch ex As Exception
            Throw
        End Try
    End Sub

    Protected Function CreateCommand() As SqlCommand
        Dim ret As SqlCommand = Nothing

        Try
            ret = db.CreateCommand

            If Not CurrentTransaction Is Nothing Then
                ret.Transaction = CurrentTransaction
            End If
        Catch ex As Exception
            Throw
        Finally

        End Try
        Return ret
    End Function
    Public Sub BeginTransaction()
        If CurrentTransaction Is Nothing Then
            CurrentTransaction = db.BeginTransaction
        End If
    End Sub
    Public Sub CommitTransaction()
        If Not CurrentTransaction Is Nothing Then
            CurrentTransaction.Commit()
            CurrentTransaction.Dispose()
            CurrentTransaction = Nothing
        End If
    End Sub
    Public Sub RollbackTransaction()
        If Not CurrentTransaction Is Nothing Then
            CurrentTransaction.Rollback()
            CurrentTransaction.Dispose()
            CurrentTransaction = Nothing
        End If
    End Sub
       Protected Overrides Sub Finalize()
        MyBase.Finalize()
    End Sub
End Class

Утилита класса

Public Class SqlConnectionUtilities    

    Public Shared Property connectionString As String

    Public Shared Function GetConnection(ByVal ServerName As String, ByVal Port As String, ByVal Database As String, ByVal username As String, ByVal password As String) As SqlConnection
        Dim connString As New StringBuilder
        Dim con As SqlConnection
        Try
            connString.Append("Server=tcp:" & ServerName & "," & Port & ";")
            connString.Append("Initial Catalog = " & Database & "; Persist Security Info=False;")
            connString.Append("User ID = " & username & ";")
            connString.Append("Password = " & password & ";")
            connString.Append("MultipleActiveResultSets = False;")
            connString.Append("Encrypt = True;TrustServerCertificate=False;Connection Timeout=30;")

            connectionString = connString.ToString

            con = New SqlConnection(connString.ToString)
            con.Open()
            Return con
        Catch ex As Exception
            Throw
        End Try
    End Function

    Public Shared Function GetConnection(ByVal Servername As String, ByVal DatabaseName As String) As SqlConnection
        Dim ConnectString As String
        Dim con As SqlConnection
        Try
            ConnectString = "Data Source=" & Servername & ";Initial Catalog=" & DatabaseName & ";Integrated Security=True"
            connectionString = ConnectString
            con = New SqlConnection(ConnectString)
            con.Open()
            Return con
        Catch ex As Exception
            Throw
        End Try
    End Function   
End class

Я могу сказать, что соединения остаются открытыми, выполнив инструкцию SQL:

SELECT 
    DB_NAME(dbid) as DBName, 
    COUNT(dbid) as NumberOfConnections,
    loginame as LoginName
FROM
    sys.sysprocesses
WHERE 
    dbid > 0
GROUP BY 
    dbid, loginame

Я устанавливаю точки останова, когда вызывается мой класс DataProxy. Я запускаю код SQL и вижу, что новое соединение открыто. Затем я снова запускаю код, когда избавляюсь от класса DataProxy, и вижу, что соединение осталось. Это будет накапливаться до тех пор, пока не будет достигнуто 101 соединение, а затем вызывает вышеуказанную ошибку. Как я неправильно обрабатываю соединения?

1 Ответ

2 голосов
/ 22 апреля 2019

System.InvalidOperationException: 'Истекло время ожидания. Время ожидания истекло до получения соединения из пула. Это могло произойти из-за того, что все пулы подключений использовались и был достигнут максимальный размер пула. '

Как я неправильно обрабатываю соединения?

Вы "протекаете" соединения. То есть у вас есть некоторый путь к коду, который открывает SqlConnection, а не закрывает / удаляет его. SqlConnection остается открытым и находится в управляемой куче. В конце концов это будет GC'd, и его финализатор закроет соединение. Но если вы потеряете 100 соединений до того, как это произойдет, вы получите эту ошибку.

Таким образом, вы должны убедиться, что ваши SqlConnections всегда закрыты с использованием блока USING или управляются каким-то другим объектом, который закрыт с помощью блока USING.

Обратите внимание, что если вы возвращаете SqlDataReader из функции, есть специальный CommandBehavior , который закрывает SqlConnection при закрытии SqlDataReader.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...