我有一个设计为多层的网站。我的代码有效,但是我注意到我的应用程序变得越大,更多的SQL数据库连接开始堆积并保持打开状态。最终导致此错误:
System.InvalidOperationException:超时已过期。超时时间 从池中获取连接之前经过的时间。这个 可能是因为所有池化连接都在使用中并且最大 池达到了大小。'
我的代码分为3层。他们是:
ConnectionAdapter
层进行连接。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个连接,然后才导致上述错误。我如何无法正确处理连接?
答案 0 :(得分:2)
System.InvalidOperationException:超时已过期。从池中获取连接之前已经过超时时间。这可能是因为所有池化连接都在使用中,并且达到了最大池大小。'
我如何无法正确处理连接?
您正在“泄漏”连接。在IE中,您有一些代码路径可以打开SqlConnection,而不会关闭/处理它。 SqlConnection保持打开状态,并位于托管堆上。 最终它将进行GC处理,并且其终结器将关闭连接。但是,如果在此之前泄漏100个连接,则会出现此错误。
因此,您需要确保您的SqlConnection始终使用USING块关闭,或者由某些用USING块关闭的 other 对象管理。
请注意,如果要从函数返回SqlDataReader,则有一个特殊的CommandBehavior,它将在SqlDataReader关闭时关闭SqlConnection。