即使关闭后,SQL Server连接仍保持打开状态

时间:2019-04-21 19:37:29

标签: asp.net sql-server vb.net

我有一个设计为多层的网站。我的代码有效,但是我注意到我的应用程序变得越大,更多的SQL数据库连接开始堆积并保持打开状态。最终导致此错误:

  

System.InvalidOperationException:超时已过期。超时时间   从池中获取连接之前经过的时间。这个   可能是因为所有池化连接都在使用中并且最大   池达到了大小。'

我的代码分为3层。他们是:

  1. 应用程序层。每次要进行CRUD时,都称为业务层。
  2. 业务层-执行业务逻辑。当它想与MS SQL db接口时,它通过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

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 个答案:

答案 0 :(得分:2)

  

System.InvalidOperationException:超时已过期。从池中获取连接之前已经过超时时间。这可能是因为所有池化连接都在使用中,并且达到了最大池大小。'

     

我如何无法正确处理连接?

您正在“泄漏”连接。在IE中,您有一些代码路径可以打开SqlConnection,而不会关闭/处理它。 SqlConnection保持打开状态,并位于托管堆上。 最终它将进行GC处理,并且其终结器将关闭连接。但是,如果在此之前泄漏100个连接,则会出现此错误。

因此,您需要确保您的SqlConnection始终使用USING块关闭,或者由某些用USING块关闭的 other 对象管理。

请注意,如果要从函数返回SqlDataReader,则有一个特殊的CommandBehavior,它将在SqlDataReader关闭时关闭SqlConnection。