【发布时间】:2019-09-11 03:44:01
【问题描述】:
我有一个设计为多层的网站。我的代码有效,但我注意到我的应用程序变得越大,SQL 数据库连接开始堆积并保持打开状态就越多。这最终会导致此错误:
System.InvalidOperationException: '超时已过期。超时 从池中获取连接之前经过的时间。这 可能已经发生,因为所有池连接都在使用中并且最大 已达到池大小。'
我的代码分为 3 层。它们是:
- 应用层。每次它要 CRUD,都是调用业务层。
- 业务层 - 执行业务逻辑。当它想与 MS SQL db 接口时,它通过
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
连接适配器
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 个连接,然后它会导致上述错误。我怎么没有正确处理连接?
【问题讨论】:
-
请记住,我们在 ADO.NET 中有 ConnectionPooling。然而,有趣的是,在所有这些代码之间,您没有发布关键的 Connect 并传递了两个或 5 个参数。我还看到一个返回 SqlConnection 的 GetDbConnection 方法,我们不知道该连接是否正确处理。
-
很抱歉。我已经添加了连接方法。参数来自 webconfig 文件。 GetDbConnection 是无关代码,从未调用过。我删除了它。感谢您查看此内容!
-
在你开始调整池行为之前,请 a) 删除所有仅包含
throw的catch块,它们 100% 毫无意义,b) 删除所有Finally块在局部变量上调用Dispose并在所述变量上用Usings 替换它们,c) 删除仅调用Base.Finalize的Finalize子程序,以及 d)SqlConnectionAdapter中的 properly implement the Disposable pattern (这样做,请考虑标准做法是在 dispose 时回滚未提交的事务,而不是提交它)。
标签: asp.net sql-server vb.net