【问题标题】:Best way to pass a connection object among forms?在表单之间传递连接对象的最佳方式?
【发布时间】:2010-10-08 04:22:59
【问题描述】:

背景:我正在将使用 MS Access 进行数据存储的 VB6 应用程序重写为使用 VB.NET 和 MS SQL Server 的应用程序。

我很好奇在我的应用程序中需要连接到数据库的不同表单之间传递连接的最佳方式。现在我已经建立了一个类来管理连接字符串以安全的方式在表单之间传递:

公共类登录凭据
    私有 unname 作为字符串
    私有密码哈希()作为字节 = {0}
    Private server_name As String '未用于访问样式数据库
    私有数据库名作为字符串
    Private st As ServerType '允许不同连接的枚举
    Private tdes As TripleDES '加密类来加密内存中的密码

    公共子新()
        unname = ""
        server_name = ""
        数据库名 = ""
        st = ServerType.stNotDefined
    结束子
    Public Sub New(ByVal 用户名作为字符串,_
                   ByVal 密码为字符串,_
                   ByVal ServerName 作为字符串,_
                   ByVal 数据库名称作为字符串,_
                   ByVal ServType 作为 ServerType)
        tdes = 新的 TripleDES
        uname = 用户名
        password_hash = tdes.Encrypt(密码)
        server_name = 服务器名
        dbname = 数据库名称
        st = 服务类型
       tdes = 没有
    结束子

    公共只读属性 Server_Type() As ServerType
        得到
            回程
        结束获取
    结束属性
    公共只读属性 CompanyName() 作为字符串
        得到
            返回 dbname.Remove(0, 4)
        结束获取
    结束属性
    公共属性 UserName() 作为字符串
        得到
            返回名称
        结束获取
        设置(ByVal 值作为字符串)
            unname = 值
        结束集
    结束属性
    公共属性密码()作为字符串
        得到
            tdes = 新的 TripleDES
            返回 tdes.Decrypt(password_hash)
            tdes = 没有
        结束获取
        设置(ByVal 值作为字符串)
            tdes = 新的 TripleDES
            password_hash = tdes.Encrypt(值)
            tdes = 没有
        结束集
    结束属性
    公共属性 ServerName() 作为字符串
        得到
            返回 server_name
        结束获取
        设置(ByVal 值作为字符串)
            server_name = 值
        结束集
    结束属性
    公共属性 DatabaseName() 作为字符串
        得到
            返回数据库名
        结束获取
        设置(ByVal 值作为字符串)
            数据库名称 = 值
        结束集
    结束属性

    公共函数 GetConnectionString() 作为字符串
        暗淡 cstring As String = ""
        tdes = 新的 TripleDES
        选择案例
            案例服务器类型.stSQLServer
                cstring = "用户 ID=" & uname & ";" &_
                        "密码=" & tdes.Decrypt(password_hash) & ";" &_
                        "初始目录=" & dbname & ";" &_
                        “数据源=”& server_name
        结束选择
        tdes = 没有
        返回字符串
    结束功能
结束类

我一直在将我的对象的引用传递给我需要连接到数据库的任何表单,如下所示:

'在表单声明中
私有 myLC​​ 作为 LoginCredentials
Public Sub New(ByRef lc As LoginCredentials)
    初始化组件()
    myLC = lc
结束子

然后我会创建一个新的连接对象,做我需要做的事情,然后关闭连接并销毁连接对象。当我不久前在 ADO 中使用 VB6 完成此操作时,连接创建的进程在连接对象被销毁时被终止,但情况似乎不再如此。现在每次我创建一个新的连接对象并连接到我的服务器时,都会创建一个新进程,然后在我关闭连接时进入睡眠状态。一段时间后,服务器将开始拒绝连接,直到我登录并终止我的应用程序创建的所有进程。显然这不是正确的,我想学习正确的方法。

在我的表单之间通过引用(或在包装类中)简单地传递相同的连接对象,让连接对象保持打开状态会更好吗?

关闭连接以使我最终不会在我的 SQL 服务器上获得一堆休眠进程的正确方法是什么? SQL Server 中是否有可以调整为在一段时间不活动后自动终止进程的设置?

您是否考虑在运行时内存中加密密码过大?

感谢您的帮助。 :)

【问题讨论】:

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


【解决方案1】:

不应该在表单之间传递连接对象。基本上,使用 SQL Server 连接时的模式是创建连接、打开连接、执行操作,然后关闭连接。

为此,您应该在某处有一个公共静态方法,该方法将生成您将在 Using 语句中使用的 SqlConnection,如下所示:

Using connection As SqlConnection = GetConnection
    ' Use connection here.

End Using

这应该可以防止进程在服务器上堆积。

【讨论】:

  • 以前从未尝试过这种方式,但它比我的想法更有意义。我会试一试,谢谢!
  • 假设你需要调用 sub 100 次,那么你需要在 sub 中打开 100 个连接。为了防止您需要更高级别的上下文来共享您的连接。否则效果很好。
  • @Middletone:我同意,但是必须采取非常严格的步骤来确保正确处理连接。仅仅建立一个连接是一个非常糟糕的主意。您应该创建连接,然后将其传递给将执行迭代的例程。
  • @Middletone:另外,如果您要多次执行该操作,那么很可能将其放入存储过程中,并且需要一次执行(来自客户端)。
  • 说得好。想想编码实践有多少变化是令人惊讶的,因为每个场景都略有不同。感谢您的意见。
【解决方案2】:

您可以使用 Using 语句,它会在完成后关闭并处理连接。

Using _conn as New SqlConnection(<connstring>)
  _conn.Open()
  'get your data'

End Using

如果你没有打电话给.Close(),那可能是问题所在。

【讨论】:

    【解决方案3】:

    我同意卡斯珀的观点。例如,如果您确实需要在页面之间共享对象以减少负载,那么您可以使用静态成员变量来执行此操作。只需确保在执行最后一条语句时关闭连接。您还可以创建一个可以在最后一个事务完成时释放的连接范围。如果您没有这样做的经验,请尽早打开和关闭您的连接,不要随意传递。

    我有一个 Web 应用程序,为了减少一些延迟,在某些情况下我会使用我为 DAL 创建的范围,这样如果子函数中有调用,它们可以使用相同的连接而不会被提升到 MSDTC。然而,这实际上只在事务系统中是必需的。

    【讨论】:

    • 谢谢,我会试一试。
    【解决方案4】:

    由于您使用的是 VB.NET,请试试这个(代码来自内存,而不是从应用程序复制):

    Namespace Helpers
    
    Public NotInheritable Class Connections
    
        Private Sub New()
    
        End Sub
    
        Public Shared Function GetConnection(ByVal connString As String) As SqlConnection
            Dim c as New SqlConnection(connString)
            c.Open
            Return c
        End Sub
    
        Public Shared Sub AdoCleanup(cn As SqlConnection, cmd As SqlCommand)
            cmd.Dispose
            cn.Close
        End Sub
    
    End Class
    
    End Namespace
    

    然后像这样使用它:

    Private Sub LoadMyData()
    
        Dim connString As String = <your conn string>
        Dim cn As SqlConnection = Helpers.Connections.GetConnection(connString)
        Dim cmd As New SqlCommand
    
        Try
            ' data access code
        Catch ex As Exception
            ' handle exception
        Finally
            Helpers.Connections.AdoCleanup(cn, cmd)
        End Try
    
    End Sub
    

    您甚至可以将获取连接字符串的代码放入 GetConnection,除非您需要灵活地打开具有不同连接字符串的多个连接。

    【讨论】:

    • 感谢您的帮助。我可以试试这个,因为在某些情况下我需要打开多个连接。做事的方法太多了!
    【解决方案5】:

    从不传递连接。

    我通常做什么,我创建一个主控制器,每个控制器都从它继承,我在那里定义了连接。 然后当我每次启动我使用(使用)的连接对象时。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-12
      • 2019-04-03
      • 1970-01-01
      • 1970-01-01
      • 2014-01-27
      • 1970-01-01
      相关资源
      最近更新 更多