使用 Class 模块将可编辑的 ADO 记录集返回到 MS Access 表单
Returning editable ADO Recordsets to an MS Access Form using a Class Module
前言:我在前端使用 SQL Server 2008 R2 后端和 MS Access 2007
我有一个 Class 模块,它 return 是我想要从 SQL 服务器获取的任何 ADO 记录集。然后我可以将其分配给任何形式 RecordSource
属性.
问题是,当我尝试编辑字段时,它在状态栏中显示 "This form is read-only"。我希望表格可以编辑。
我有两种形式
- FormEntities
- FormEntitiesEdit
FormEntitiesEdit 表单不使用 Class 模块。相反,所有代码都在表单本身中。
class 模块的目的是避免冗余,只是能够使用 Class 模块轻松地从 SQL 服务器获取记录集。
首先是我的全局模块
'Default error message. 'eh' stands for error handler
Public eh As String
'Global variables for universal use
Public conn As ADODB.Connection
Public rs As ADODB.Recordset
Public com As ADODB.Command
第二个是 CLASS 模块(名称是 cADO)。
这个 CLASS 模块使用上面的 conn 连接对象
Option Explicit
Private Const CONST_LockType = 3
Private Const CONST_CursorType = 1
Private Const CONST_CursorLocationServer = 3
Private Const CONST_CursorLocationClient = 2
Private m_Recordset As ADODB.Recordset
'For Public Recordset function
Private cSQL$
'**********************************************************************
Public Function cGetRecordset(ByRef sql) As ADODB.Recordset
Set m_Recordset = New ADODB.Recordset
cSQL = sql
cOpenRecordset
Set cGetRecordset = m_Recordset
End Function
'**********************************************************************
Public Property Set Recordset(Value As ADODB.Recordset)
'Assigns private variable a property
If Not Value Is Nothing Then Set m_Recordset = Value
End Property
'**********************************************************************
Public Property Get Recordset() As ADODB.Recordset
'Reads the recordset from the private variable and assigns to new object variable
Set Recordset = m_Recordset
End Property
'********************************** PRIVATE SECTION **********************************
Private Sub cOpenRecordset()
On Error GoTo eh
'Ensures that if a recordset is opened from previously that it closes before opening a new one
If m_Recordset.State adStateClosed Then m_Recordset.Close
Set m_Recordset.ActiveConnection = conn
With m_Recordset
.LockType = CONST_LockType
.CursorType = CONST_CursorType
.CursorLocation = CONST_CursorLocationClient
.Source = cSQL
.Open .Source
End With
If Not m_Recordset.EOF Then m_Recordset.MoveFirst
Exit Sub
eh:
eh = "Error # " & Str(Err.Number) & " was generated by " & _
Err.Source & Chr(13) & Err.Description
MsgBox eh, vbCritical, "Open Recordset System"
End Sub
'**********************************************************************
Private Sub cCloseRecordset()
m_Recordset.Close
Set m_Recordset = Nothing
End Sub
'**********************************************************************
Private Sub Class_Terminate()
If Not (m_Recordset Is Nothing) Then Set m_Recordset = Nothing
End Sub
第三个是我的 FormEntities 表单背后的代码(使用 cADO CLASS 模块)
Option Explicit
Dim db As cADO
'**********************************************************************
Private Sub Form_Current()
LoadTab
End Sub
'**********************************************************************
Private Sub Form_Load()
Set db = New cADO
FetchRecordSource
End Sub
'**********************************************************************
Private Sub FetchRecordSource()
db.cGetRecordset ("SELECT * FROM dbo.Entities")
Set Forms("fEntities").Recordset = db.Recordset
End Sub
第四个也是最后一个是 FormEntitiesEdit 表单背后的代码(此表单不使用 CLASS 模块,我可以编辑它)
Option Compare Database
Option Explicit
Dim rsEntity As New ADODB.Recordset
'**********************************************************************
Private Sub Form_Load()
FetchRecordSource
End Sub
'**********************************************************************
Private Sub FetchRecordSource()
Set rsEntity.ActiveConnection = conn
'Sets the record source for the main form
With rsEntity
.LockType = adLockOptimistic
.CursorType = adOpenKeyset
.CursorLocation = adUseClient
.Source = "SELECT * FROM dbo.Entities"
.Open .Source
End With
Set Forms("fEntitiesEdit").Recordset = rsEntity
End Sub
'**********************************************************************
Private Sub CloseConn()
rsEntity.Close
End Sub
如果 Access Jet SQL 可以执行 SQL,我会将此表单绑定到链接 Table。但是,我使用的是 Jet SQL 无法执行的分层(递归)查询,因此我必须绕过绑定表单到 Linked Table 的想法。
我将表单上的 .Allow Edits 和 .AllowAdditions 设置为 true。
我还尝试将 ADO 记录集上的 .LockType 更改为
- adOpenKeyset 和
- adOpenDynamic
我的 LockType 是 adLockOptimistic
如您所见,属性设置正确,可以编辑记录集 return。
我知道这是真的,因为当我使用具有相同属性的 FormEntitiesEdit 表单时,它允许我编辑字段。但是当我将 Class 模块用于 return(使用相同的属性)时,它说它是只读的。
这很重要,因为如您所见,使用 Class 模块要简单得多,只需要它 return 一个可编辑的记录集。
有人有想法吗?有什么建议吗?
问题出在 class' cOpenRecordset()
方法中:
.CursorLocation = CONST_CursorLocationClient
这里是你分配常量值的地方...
Private Const CONST_CursorLocationServer = 3
Private Const CONST_CursorLocationClient = 2
不幸的是,您交换了这两个值。这是 ADODB.CursorLocationEnum
常量 ...
adUseClient = 3
adUseServer = 2
所以您的 class 正在有效地执行此操作...
.CursorLocation = adUseServer
但是如果您希望记录集可编辑,则需要客户端游标。我认为如果您只是重新定义常量,您的 class 方法可能会奏效,否则您会暴露出不同的问题...
Private Const CONST_CursorLocationClient = 3
FormEntitiesEdit 是可编辑的,因为您在那里使用了正确的常量...
.CursorLocation = adUseClient
前言:我在前端使用 SQL Server 2008 R2 后端和 MS Access 2007
我有一个 Class 模块,它 return 是我想要从 SQL 服务器获取的任何 ADO 记录集。然后我可以将其分配给任何形式 RecordSource
属性.
问题是,当我尝试编辑字段时,它在状态栏中显示 "This form is read-only"。我希望表格可以编辑。
我有两种形式
- FormEntities
- FormEntitiesEdit
FormEntitiesEdit 表单不使用 Class 模块。相反,所有代码都在表单本身中。
class 模块的目的是避免冗余,只是能够使用 Class 模块轻松地从 SQL 服务器获取记录集。
首先是我的全局模块
'Default error message. 'eh' stands for error handler Public eh As String 'Global variables for universal use Public conn As ADODB.Connection Public rs As ADODB.Recordset Public com As ADODB.Command
第二个是 CLASS 模块(名称是 cADO)。 这个 CLASS 模块使用上面的 conn 连接对象
Option Explicit Private Const CONST_LockType = 3 Private Const CONST_CursorType = 1 Private Const CONST_CursorLocationServer = 3 Private Const CONST_CursorLocationClient = 2 Private m_Recordset As ADODB.Recordset 'For Public Recordset function Private cSQL$ '********************************************************************** Public Function cGetRecordset(ByRef sql) As ADODB.Recordset Set m_Recordset = New ADODB.Recordset cSQL = sql cOpenRecordset Set cGetRecordset = m_Recordset End Function '********************************************************************** Public Property Set Recordset(Value As ADODB.Recordset) 'Assigns private variable a property If Not Value Is Nothing Then Set m_Recordset = Value End Property '********************************************************************** Public Property Get Recordset() As ADODB.Recordset 'Reads the recordset from the private variable and assigns to new object variable Set Recordset = m_Recordset End Property '********************************** PRIVATE SECTION ********************************** Private Sub cOpenRecordset() On Error GoTo eh 'Ensures that if a recordset is opened from previously that it closes before opening a new one If m_Recordset.State adStateClosed Then m_Recordset.Close Set m_Recordset.ActiveConnection = conn With m_Recordset .LockType = CONST_LockType .CursorType = CONST_CursorType .CursorLocation = CONST_CursorLocationClient .Source = cSQL .Open .Source End With If Not m_Recordset.EOF Then m_Recordset.MoveFirst Exit Sub eh: eh = "Error # " & Str(Err.Number) & " was generated by " & _ Err.Source & Chr(13) & Err.Description MsgBox eh, vbCritical, "Open Recordset System" End Sub '********************************************************************** Private Sub cCloseRecordset() m_Recordset.Close Set m_Recordset = Nothing End Sub '********************************************************************** Private Sub Class_Terminate() If Not (m_Recordset Is Nothing) Then Set m_Recordset = Nothing End Sub
第三个是我的 FormEntities 表单背后的代码(使用 cADO CLASS 模块)
Option Explicit Dim db As cADO '********************************************************************** Private Sub Form_Current() LoadTab End Sub '********************************************************************** Private Sub Form_Load() Set db = New cADO FetchRecordSource End Sub '********************************************************************** Private Sub FetchRecordSource() db.cGetRecordset ("SELECT * FROM dbo.Entities") Set Forms("fEntities").Recordset = db.Recordset End Sub
第四个也是最后一个是 FormEntitiesEdit 表单背后的代码(此表单不使用 CLASS 模块,我可以编辑它)
Option Compare Database Option Explicit Dim rsEntity As New ADODB.Recordset '********************************************************************** Private Sub Form_Load() FetchRecordSource End Sub '********************************************************************** Private Sub FetchRecordSource() Set rsEntity.ActiveConnection = conn 'Sets the record source for the main form With rsEntity .LockType = adLockOptimistic .CursorType = adOpenKeyset .CursorLocation = adUseClient .Source = "SELECT * FROM dbo.Entities" .Open .Source End With Set Forms("fEntitiesEdit").Recordset = rsEntity End Sub '********************************************************************** Private Sub CloseConn() rsEntity.Close End Sub
如果 Access Jet SQL 可以执行 SQL,我会将此表单绑定到链接 Table。但是,我使用的是 Jet SQL 无法执行的分层(递归)查询,因此我必须绕过绑定表单到 Linked Table 的想法。
我将表单上的 .Allow Edits 和 .AllowAdditions 设置为 true。
我还尝试将 ADO 记录集上的 .LockType 更改为
- adOpenKeyset 和
- adOpenDynamic
我的 LockType 是 adLockOptimistic
如您所见,属性设置正确,可以编辑记录集 return。
我知道这是真的,因为当我使用具有相同属性的 FormEntitiesEdit 表单时,它允许我编辑字段。但是当我将 Class 模块用于 return(使用相同的属性)时,它说它是只读的。
这很重要,因为如您所见,使用 Class 模块要简单得多,只需要它 return 一个可编辑的记录集。
有人有想法吗?有什么建议吗?
问题出在 class' cOpenRecordset()
方法中:
.CursorLocation = CONST_CursorLocationClient
这里是你分配常量值的地方...
Private Const CONST_CursorLocationServer = 3
Private Const CONST_CursorLocationClient = 2
不幸的是,您交换了这两个值。这是 ADODB.CursorLocationEnum
常量 ...
adUseClient = 3
adUseServer = 2
所以您的 class 正在有效地执行此操作...
.CursorLocation = adUseServer
但是如果您希望记录集可编辑,则需要客户端游标。我认为如果您只是重新定义常量,您的 class 方法可能会奏效,否则您会暴露出不同的问题...
Private Const CONST_CursorLocationClient = 3
FormEntitiesEdit 是可编辑的,因为您在那里使用了正确的常量...
.CursorLocation = adUseClient