将记录集存储为类的实例?

时间:2011-05-13 18:51:14

标签: excel vba recordset

有以下情况。我有一些表单,基本上有一些dropbox,列表等。我用ms sql db中的记录填充它们。但是,有没有办法只查询数据库一次并在整个应用程序生命周期中将记录存储为类的实例,而不是每次用户打开表单时查询?

连接如下:

Sub connection_test()

    Dim Cn As ADODB.Connection
    Dim Rs As ADODB.Recordset
    Dim stSQL As String

    stSQL = "SELECT * FROM dbo.Client"

    Set Cn = New ADODB.Connection

    With Cn
        .CursorLocation = adUseClient
        .Open CONNECTION_STRING
        .CommandTimeout = 0
        Set Rs = .Execute(stSQL)
    End With

    Rs.Close
    Cn.Close
    Set Rs = Nothing
    Set Cn = Nothing
End Sub

有人可以为我建议一个解决方案吗?我可以看到来自交通繁忙的所有问题,而且大部分都是不必要的。

2 个答案:

答案 0 :(得分:5)

如果您只想提供记录集,请将变量调整为标准模块中的公共

Public Rs As ADODB.Recordset

Sub connection_test()

    Dim Cn As ADODB.Connection
    Dim sSQL As String

    If Rs.State = adStateClosed Then
        sSQL = "SELECT * FROM dbo.Client"

        Set Cn = New ADODB.Connection

        With Cn
            .CursorLocation = adUseClient
            .Open CONNECTION_STRING
            .CommandTimeout = 0
            Set Rs = .Execute(sSQL)
        End With
    End If

End Sub

现在Rs将在项目的任何地方可用。您可以随时运行connection_test,如果它关闭了记录集,它将创建它。如果没有,你很高兴。

一般来说,我的方法是创建自定义类。我将创建一个CClient类,从记录集(或其他地方)填充它,使用业务逻辑操作对象,然后将新值写回数据库。这样,我的业务逻辑都不依赖于我正在使用ado的事实。我可以切换到文本文件或Excel工作表作为数据存储,而不必担心代码中的所有依赖项。

例如,假设我有一个Access表:

 ClientID, Autonumber
 ContactFirst, String
 ContactLast, String
 Company, String
 CityState, String
 Volume, Double

我创建了一个CClient类,其中包含表格中每个字段的属性。我还创建了一个CClients类来保存所有CClient实例。在标准模块中,您可能会有类似这样的内容

Public gclsClients As CClients

Sub Main()

    Set gclsClients = New CClients

    'Fill the class
    gclsClients.FillFromRS

    'change some value
    gclsClients.Client(1).Volume = 100

    'write back to the database
    gclsClients.WriteToDB

End Sub

在我更改一个客户端的数量的情况下,您将拥有更多代码来调用您的用户表单等。基础知识是,加载类,执行您需要的任何操作,然后将类数据写回数据库。我不会向您展示所有类代码,但在CClients中

Public Sub FillFromRS()

    Dim cn As ADODB.Connection
    Dim rs As ADODB.Recordset
    Dim clsClient As CClient

    Const sSQL As String = "SELECT * FROM tblClient"

    Set cn = New ADODB.Connection

    cn.Open msCON
    Set rs = cn.Execute(sSQL)

    If Not rs.BOF And Not rs.EOF Then
        rs.MoveFirst
        Do While Not rs.EOF
            Set clsClient = New CClient
            With clsClient
                .ClientID = rs.Fields("ClientID").Value
                .ContactFirst = rs.Fields("ContactFirst").Value
                .ContactLast = rs.Fields("ContactLast").Value
                .Company = rs.Fields("Company").Value
                .CityState = rs.Fields("CityState").Value
                .Volume = rs.Fields("Volume").Value
            End With
            Me.Add clsClient
            rs.MoveNext
        Loop
    End If

End Sub

此方法从数据库获取数据并填充一堆CClient实例。同样在CClients中

Public Sub WriteToDB()

    Dim cn As ADODB.Connection
    Dim rs As ADODB.Recordset
    Dim clsClient As CClient
    Dim sSQL As String

    Set cn = New ADODB.Connection

    cn.Open msCON

    For Each clsClient In Me
        sSQL = BuildUpdateSQL(clsClient)
        cn.Execute sSQL
    Next clsClient

End Sub

Private Function BuildUpdateSQL(clsClient As CClient)

    Dim sReturn As String

    With clsClient
        sReturn = "UPDATE tblClient SET ContactFirst = '" & .ContactFirst & "',"
        sReturn = sReturn & " ContactLast = '" & .ContactLast & "',"
        sReturn = sReturn & " Company = '" & .Company & "',"
        sReturn = sReturn & " CityState = '" & .CityState & "',"
        sReturn = sReturn & " Volume = " & .Volume
        sReturn = sReturn & " WHERE ClientID = " & .ClientID & ";"
    End With

    BuildUpdateSQL = sReturn

End Function

此方法遍历所有CClient实例,创建UPDATE sql语句并执行它。您将要在CClient中实现某种IsDirty属性,以便您只更新那些更改了某些内容的客户端。其余的CClients和CClient都是基本的类模块。

你可以调用WriteToDB很多或者一点点。在某些应用程序中,只要有变化,我就会编写它。在其他情况下,我只在工作簿关闭时回写到数据库。这取决于您的应用程序的流程。真正的美妙之处在于,如果您从Access数据库更改为用于数据存储的文本文件,您只需要更改为CClients中的方法。您的所有其余代码都会消耗CClients,而不关心数据的存在位置。

您可以在此处查看工作簿和Access数据库http://www.dailydoseofexcel.com/excel/ClientClassExample.zip

答案 1 :(得分:4)

阅读已断开连接的记录集here

该文章确实包含的一件事(在示例代码中),但不强调的是您必须使用adLockBatchOptimistic。您必须像他们一样使用adOpenForwardOnly。当然,您的Recordset对象必须在sub之外有一个范围。我这样做:

Function connection_test() as ADODB.Recordset

Dim Cn As ADODB.Connection
Dim Rs As ADODB.Recordset

Dim stSQL As String

    stSQL = "SELECT * FROM dbo.Client"
    Set Cn = New ADODB.Connection

    With Cn
        .Open CONNECTION_STRING
        .CommandTimeout = 0
    End With

    With Rs
        .CursorLocation = adUseClient
        .LockType = adLockBatchOptimistic
        .Open stSQL
        Set .ActiveConnection = Nothing 
    End With

    Set connection_test = Rs

    'DON'T Close the Recordset
    'Rs.Close
    Cn.Close
    'Destroying the local instance is fine, though
    Set Rs = Nothing
    Set Cn = Nothing
End Function 

当然 你想添加错误处理;)。然后在调用代码中声明一个Recordset对象,并通过调用Function实例化它。