正确处理和关闭数据库对象的包装器

Wrapper that properly disposes and closes database objects

我有一个简单的辅助函数,可以在 SQL 服务器数据库和 returns 数据集中查找数据。

我的网络应用程序中很多不同的地方都使用了这个函数。

在大多数情况下,它工作得很好。但是当有很多并发用户同时连接时,我偶尔会得到这样的错误:

DBUtilities.getDataSet: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections.

发生这种情况时,我在数据库服务器事件日志中看不到任何错误,只在 Web 服务器事件日志中看到。

在阅读了有关垃圾收集和对象处理的内容后,我开始认为可能是数据库连接未关闭或未正确处理。

我想知道,是否有一种方法可以将这段代码放入某种类型的包装器中,以帮助正确处理数据库对象?

这也是我指的功能:

Public Overloads Function getDataSet(ByVal commandText As String, Optional ByVal tableName As String = "") As DataSet

    Dim ds As New DataSet
    Dim conn As New SqlConnection(myConnStr)

    Try
        Dim cmd As New SqlCommand(commandText, conn)
        cmd.CommandTimeout = 30
        cmd.CommandType = CommandType.Text
        Dim da As New SqlDataAdapter
        da.SelectCommand = cmd
        conn.Open()

        If String.IsNullOrEmpty(tableName) Then
            da.Fill(ds)
        Else
            da.Fill(ds, tableName)
        End If
    Catch ex As Exception
        Throw New Exception("AppDataMethods.getDataSet: " & ex.Message & ", cmdText = " & commandText)
    Finally
        conn.Close()
        conn = Nothing
    End Try
    Return ds

End Function

Close() 调用在 Finally 块中,所以你没问题。现代代码倾向于更喜欢 Using 块,而 conn = Nothing 行在 VB.Net 中根本没有帮助(它在 VB6/VBScript 中需要,但不再需要),但是什么你有应该没问题。如果您使用的是 DataApater 的 Fill() 方法,则也不需要调用 conn.Open()

对我来说最可怕的事情是要求commandText字符串,无法接受单独的参数数据。这实际上迫使您在其他代码中构建非常不安全的 SQL 字符串,或者仍然鼓励它作为处理 SQL 的默认方式,即使 Overloads 声明在其他地方说明了这一点。

我自己的数据方法往往总是需要传递一个SqlParameter数组,即使对于那些没有参数的查询(使用空数组)也是如此,以确保没有其他程序员可以在不了解查询参数的情况下使用我的数据库。或者,我有时使用函数式方法,而是使用 delegate/lambda 参数来分配参数。

我也倾向于在专用于数据库访问的新模块中将此方法构建为 Private,然后为每个现有的方法创建 public 方法查询。如果您有很多查询,这也可以是单独 class 库项目中的 Friend 方法,因此您有多个 class 实体或模块来将查询组织成逻辑组。

Private Overloads Function getDataSet(ByVal commandText As String, parameters As SqlParameter(), Optional ByVal tableName As String = "") As DataSet
    Dim result As New DataSet
    Using conn As New  SqlConnection(myConnStr), _
          cmd As New SqlCommand(commandText, conn), _
          da As New SqlDataAdapter(cmd)

        cmd.CommandTimeout = 30
        If parameters IsNot Nothing AndAlso parameters.Length > 0 Then
            cmd.Parameters.AddRange(parameters)
        End If

        If String.IsNullOrWhitespace(tableName) Then tableName = "Table1"
        da.Fill(result, tableName)

    End Using
    Return result    
End Function

我还删除了 Catch 块,因为它所做的唯一另一件事就是重新抛出与您已有的异常类似的异常。您丢失了 CommandText,但是如果您对每个查询都有专门的方法并使用参数化查询(如其他地方所建议的那样),那么 CommandText 中很少有您无法直接从堆栈跟踪中获取的信息。