I've seen multi-tiered classes used in other languages as the main form of interaction with a db. The idea is that it if you create a class for each table you can use one line methods to do what we now write whole subs for. The problem for us, as I see it, is that these classes would take a long time to write so only people writing big applications can do it. However theoretically all the info to write the classes is in the database so you should be able to get the classes to write themselves.
Last night I finally made a start with the code below. At the moment I have only included one method (Dump data to spreadsheet). On my machine it is "working" in that it creates a very simple class for each table to any database you connect to.
If anybody wants to try it you need to add the required references and then change the path of the Access database in the GetDbFullPath function then run WriteAllClasses.
In needs some thinking about so I am looking for suggestions/ideas to bounce around and see where to take this eg. what methods and properties to include.
' Carl Mackinder
'
' Class Writer Module for MS Access - Requires :-
' Reference to Microsoft ActiveX Object 2.x
' Reference to Microsoft VB extensibility
' Trust access to VB project checked in macro security
'
Private Function GetDbFullPath()
GetDbFullPath = "c:\EDI\EDI.mdb" ' #### CHANGE DB PATH AND RUN WriteAllClasses ####
End Function
Public Sub WriteAllClasses()
Dim Cn As ADODB.Connection
Dim RsTables As ADODB.Recordset
Dim RsFields As ADODB.Recordset
Call CnAccess(Cn)
Set RsTables = GetTables(Cn)
RsTables.MoveFirst
Do While Not RsTables.EOF
Set RsFields = GetTableFields(Cn, RsTables(2))
Call WriteClass(RsTables(2), RsFields)
RsTables.MoveNext
Loop
Call WriteClassRefs(RsTables)
Call WriteTestMod(RsTables)
End Sub
Public Sub CnAccess(ByRef Cn As ADODB.Connection)
Dim UserId As String
Dim Password As String
UserId = ""
Password = ""
Set Cn = New ADODB.Connection
Cn.Open "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & GetDbFullPath & ";", UserId, Password
End Sub
Private Sub WriteClass(TableName As String, RsFields As ADODB.Recordset)
Dim VBComp As VBComponent
Dim VBCodeMod As CodeModule
Dim LineNum As Long
Dim FieldString As String
FieldString = "Private Cn as ADODB.Connection" & Chr(13) & "Private Rs as ADODB.Recordset" & Chr(13)
If ModExists("c" & TableName) Then ModDelete ("c" & TableName)
Set VBComp = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_ClassModule)
VBComp.Name = "c" & TableName
Set VBCodeMod = VBComp.CodeModule
RsFields.MoveFirst
'
Do While Not RsFields.EOF
FieldString = FieldString & "Private F" & Trim(RsFields.Fields(3)) & " " & GetFieldType(RsFields.Fields(11)) & Chr(13)
RsFields.MoveNext
Loop
FieldString = FieldString & DumpToSheetMethod(TableName)
LineNum = VBCodeMod.CountOfLines + 1
VBCodeMod.InsertLines LineNum, FieldString
End Sub
Private Function GetTables(Cn As ADODB.Connection) As ADODB.Recordset
Set GetTables = New ADODB.Recordset
Set GetTables = Cn.OpenSchema(adSchemaTables, Array(Empty, Empty, Empty, "Table"))
End Function
Private Function GetTableFields(Cn As ADODB.Connection, TableName As String) As ADODB.Recordset
Set GetTableFields = New ADODB.Recordset
Set GetTableFields = Cn.OpenSchema(adSchemaColumns, Array(Empty, Empty, TableName))
End Function
Private Function ModExists(ModName As String) As Boolean
On Error Resume Next
ModExists = Len(ThisWorkbook.VBProject.VBComponents(ModName).Name) <> 0
End Function
Private Sub ModDelete(ModName As String)
Dim VBComp As VBComponent
Set VBComp = ThisWorkbook.VBProject.VBComponents(ModName)
ThisWorkbook.VBProject.VBComponents.Remove VBComp
Set VBComp = Nothing
End Sub
Private Function GetFieldType(FieldType As Integer) As String
Select Case FieldType
Case 130
GetFieldType = "as String"
Case 3
GetFieldType = "as Integer"
Case 5
GetFieldType = "as Double"
Case 11
GetFieldType = "as Boolean"
Case 7
GetFieldType = "as Date"
End Select
End Function
Private Sub RemoveClasses()
' Removes all classes from this workbook
Dim VBComp As VBComponent
Dim VBCodeMod As CodeModule
For Each VBComp In ThisWorkbook.VBProject.VBComponents
If VBComp.Type = vbext_ct_ClassModule Then
ThisWorkbook.VBProject.VBComponents.Remove VBComp
End If
Next VBComp
End Sub
Private Sub WriteClassRefs(RsTables As ADODB.Recordset)
' Overwrites the ClassRefs standard module which dimensions an instance of each class
Dim VBComp As VBComponent
Dim VBCodeMod As CodeModule
Dim LineNum As Long
Dim FieldString As String
FieldString = "' This sub references all the Access classes" & Chr(13)
If ModExists("ClassRefs") Then ModDelete ("ClassRefs")
Set VBComp = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_StdModule)
VBComp.Name = "ClassRefs"
Set VBCodeMod = VBComp.CodeModule
RsTables.MoveFirst
Do While Not RsTables.EOF
FieldString = FieldString & "Public Obj" & RsTables.Fields(2) & " As New c" & RsTables.Fields(2) & " " & Chr(13)
RsTables.MoveNext
Loop
LineNum = VBCodeMod.CountOfLines + 1
VBCodeMod.InsertLines LineNum, FieldString
End Sub
Private Function DumpToSheetMethod(TableName As String) As String
DumpToSheetMethod = "Public Sub DumpToSheet(DumpRange As Range)" & Chr(13) & _
"Call CnAccess(Cn)" & Chr(13) & _
"Set Rs = New ADODB.Recordset" & Chr(13) & _
"Rs.Open " & Chr(34) & TableName & Chr(34) & ",Cn" & Chr(13) & _
"DumpRange.CopyFromRecordset Rs" & Chr(13) & _
"Rs.Close" & Chr(13) & _
"Set Rs = Nothing" & Chr(13) & _
"End Sub" & Chr(13)
End Function
Private Sub WriteTestMod(RsTables As ADODB.Recordset)
' Overwrites the Test standard module with a sub for each table testing the dump method
Dim VBComp As VBComponent
Dim VBCodeMod As CodeModule
Dim LineNum As Long
Dim FieldString As String
FieldString = "' Test sub for DumpToSheet method " & Chr(13) & Chr(13)
If ModExists("Test") Then ModDelete ("Test")
Set VBComp = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_StdModule)
VBComp.Name = "Test"
Set VBCodeMod = VBComp.CodeModule
RsTables.MoveFirst
Do While Not RsTables.EOF
FieldString = FieldString & "Sub TEST" & RsTables.Fields(2) & "()" & Chr(13) & _
"Obj" & RsTables.Fields(2) & ".DumpToSheet(Worksheets(1).range(" & Chr(34) & "a1" & Chr(34) & "))" & Chr(13) & _
"End sub" & Chr(13)
RsTables.MoveNext
Loop
LineNum = VBCodeMod.CountOfLines + 1
VBCodeMod.InsertLines LineNum, FieldString
End Sub
Display More
Cheers,
Carl