How do you use version control with Access development?

后端 未结 20 1699
慢半拍i
慢半拍i 2020-11-22 12:55

I\'m involved with updating an Access solution. It has a good amount of VBA, a number of queries, a small amount of tables, and a few forms for data entry & report gene

相关标签:
20条回答
  • 2020-11-22 13:01

    Based on the ideas of this post and similar entries in some blogs I have wrote an application that works with mdb and adp file formats. It import/export all database objects (including tables, references, relations and database properties) to plain text files. With those files you can work with a any source version control. Next version will allow import back the plain text files to the database. There will be also a command line tool

    You can download the application or the source code from: http://accesssvn.codeplex.com/

    regards

    0 讨论(0)
  • 2020-11-22 13:03

    For those using Access 2010, SaveAsText is not a visible method in Intellisense but it appears to be a valid method, as Arvin Meyer's script mentioned earlier worked fine for me.

    Interestingly, SaveAsAXL is new to 2010 and has the same signature as SaveAsText, though it appears it will only work with web databases, which require SharePoint Server 2010.

    0 讨论(0)
  • 2020-11-22 13:04

    We had the same issue a while ago.

    Our first try was a third-party tool which offers a proxy of the SourceSafe API for Subversion to be used with MS Access and VB 6. The Tool can be found here.

    As we were not that satisfied with that tool we switched over to Visual SourceSafe and the VSS Acces Plugin.

    0 讨论(0)
  • 2020-11-22 13:05

    Resurrecting an old thread but this is a good one. I've implemented the two scripts (compose.vbs / decompose.vbs) for my own project and ran into a problem with old .mdb files:

    It stalls when it gets to a form that includes the code:

    NoSaveCTIWhenDisabled =1
    

    Access says it has a problem and that's the end of the story. I ran some tests and played around trying to get around this issue and found this thread with a work around at the end:

    Can't create database

    Basically (in case the thread goes dead), you take the .mdb and do a "Save as" to the new .accdb format. Then the source safe or compose/decompose stuff will work. I also had to play around for 10 minutes to get the right command line syntax for the (de)compose scripts to work right so here's that info as well:

    To compose (say your stuff is located in C:\SControl (create a sub folder named Source to store the extracted files):

    '(to extract for importing to source control)
    cscript compose.vbs database.accdb     
    
    '(to rebuild from extracted files saved from an earlier date)
    cscript decompose.vbs database.accdb C:\SControl\Source\
    

    That's it!

    The versions of Access where I've experienced the problem above include Access 2000-2003 ".mdb" databases and fixed the problem by saving them into the 2007-2010 ".accdb" formats prior to running the compose/decompose scripts. After the conversion the scripts work just fine!

    0 讨论(0)
  • 2020-11-22 13:07

    Text-file only solution (queries, tables and relationships included)

    I have altered the Oliver's pair of scripts so that they export/import relationships, tables and queries in addition to modules, classes, forms and macros. Everything is saved into plaintext files, so there is no database file created to be stored with the text files in version control.

    Export into text files (decompose.vbs)

    ' Usage:
    '  cscript decompose.vbs <input file> <path>
    
    ' Converts all modules, classes, forms and macros from an Access Project file (.adp) <input file> to
    ' text and saves the results in separate files to <path>.  Requires Microsoft Access.
    Option Explicit
    
    Const acForm = 2
    Const acModule = 5
    Const acMacro = 4
    Const acReport = 3
    Const acQuery = 1
    Const acExportTable = 0
    
    ' BEGIN CODE
    Dim fso, relDoc, ACCDBFilename, sExportpath
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set relDoc = CreateObject("Microsoft.XMLDOM")
    
    If (Wscript.Arguments.Count = 0) Then
        MsgBox "Please provide the .accdb database file", vbExclamation, "Error"
        Wscript.Quit()
    End If
    ACCDBFilename = fso.GetAbsolutePathName(Wscript.Arguments(0))
    
    If (Wscript.Arguments.Count = 1) Then
     sExportpath = ""
    Else
     sExportpath = Wscript.Arguments(1)
    End If
    
    
    exportModulesTxt ACCDBFilename, sExportpath
    
    If (Err <> 0) And (Err.Description <> Null) Then
        MsgBox Err.Description, vbExclamation, "Error"
        Err.Clear
    End If
    
    Function exportModulesTxt(ACCDBFilename, sExportpath)
        Dim myComponent, sModuleType, sTempname, sOutstring
        Dim myType, myName, myPath, hasRelations
        myType = fso.GetExtensionName(ACCDBFilename)
        myName = fso.GetBaseName(ACCDBFilename)
        myPath = fso.GetParentFolderName(ACCDBFilename)
    
        'if no path was given as argument, use a relative directory
        If (sExportpath = "") Then
            sExportpath = myPath & "\Source"
        End If
        'On Error Resume Next
        fso.DeleteFolder (sExportpath)
        fso.CreateFolder (sExportpath)
        On Error GoTo 0
    
        Wscript.Echo "starting Access..."
        Dim oApplication
        Set oApplication = CreateObject("Access.Application")
        Wscript.Echo "Opening " & ACCDBFilename & " ..."
        If (Right(ACCDBFilename, 4) = ".adp") Then
         oApplication.OpenAccessProject ACCDBFilename
        Else
         oApplication.OpenCurrentDatabase ACCDBFilename
        End If
        oApplication.Visible = False
    
        Wscript.Echo "exporting..."
        Dim myObj
        For Each myObj In oApplication.CurrentProject.AllForms
            Wscript.Echo "Exporting FORM " & myObj.FullName
            oApplication.SaveAsText acForm, myObj.FullName, sExportpath & "\" & myObj.FullName & ".form.txt"
            oApplication.DoCmd.Close acForm, myObj.FullName
        Next
        For Each myObj In oApplication.CurrentProject.AllModules
            Wscript.Echo "Exporting MODULE " & myObj.FullName
            oApplication.SaveAsText acModule, myObj.FullName, sExportpath & "\" & myObj.FullName & ".module.txt"
        Next
        For Each myObj In oApplication.CurrentProject.AllMacros
            Wscript.Echo "Exporting MACRO " & myObj.FullName
            oApplication.SaveAsText acMacro, myObj.FullName, sExportpath & "\" & myObj.FullName & ".macro.txt"
        Next
        For Each myObj In oApplication.CurrentProject.AllReports
            Wscript.Echo "Exporting REPORT " & myObj.FullName
            oApplication.SaveAsText acReport, myObj.FullName, sExportpath & "\" & myObj.FullName & ".report.txt"
        Next
        For Each myObj In oApplication.CurrentDb.QueryDefs
            Wscript.Echo "Exporting QUERY " & myObj.Name
            oApplication.SaveAsText acQuery, myObj.Name, sExportpath & "\" & myObj.Name & ".query.txt"
        Next
        For Each myObj In oApplication.CurrentDb.TableDefs
         If Not Left(myObj.Name, 4) = "MSys" Then
          Wscript.Echo "Exporting TABLE " & myObj.Name
          oApplication.ExportXml acExportTable, myObj.Name, , sExportpath & "\" & myObj.Name & ".table.txt"
          'put the file path as a second parameter if you want to export the table data as well, instead of ommiting it and passing it into a third parameter for structure only
         End If
        Next
    
        hasRelations = False
        relDoc.appendChild relDoc.createElement("Relations")
        For Each myObj In oApplication.CurrentDb.Relations  'loop though all the relations
        If Not Left(myObj.Name, 4) = "MSys" Then
         Dim relName, relAttrib, relTable, relFoTable, fld
         hasRelations = True
    
         relDoc.ChildNodes(0).appendChild relDoc.createElement("Relation")
         Set relName = relDoc.createElement("Name")
         relName.Text = myObj.Name
         relDoc.ChildNodes(0).LastChild.appendChild relName
    
         Set relAttrib = relDoc.createElement("Attributes")
         relAttrib.Text = myObj.Attributes
         relDoc.ChildNodes(0).LastChild.appendChild relAttrib
    
         Set relTable = relDoc.createElement("Table")
         relTable.Text = myObj.Table
         relDoc.ChildNodes(0).LastChild.appendChild relTable
    
         Set relFoTable = relDoc.createElement("ForeignTable")
         relFoTable.Text = myObj.ForeignTable
         relDoc.ChildNodes(0).LastChild.appendChild relFoTable
    
         Wscript.Echo "Exporting relation " & myObj.Name & " between tables " & myObj.Table & " -> " & myObj.ForeignTable
    
         For Each fld In myObj.Fields   'in case the relationship works with more fields
          Dim lf, ff
          relDoc.ChildNodes(0).LastChild.appendChild relDoc.createElement("Field")
    
          Set lf = relDoc.createElement("Name")
          lf.Text = fld.Name
          relDoc.ChildNodes(0).LastChild.LastChild.appendChild lf
    
          Set ff = relDoc.createElement("ForeignName")
          ff.Text = fld.ForeignName
          relDoc.ChildNodes(0).LastChild.LastChild.appendChild ff
    
          Wscript.Echo "  Involving fields " & fld.Name & " -> " & fld.ForeignName
         Next
        End If
        Next
        If hasRelations Then
         relDoc.InsertBefore relDoc.createProcessingInstruction("xml", "version='1.0'"), relDoc.ChildNodes(0)
         relDoc.Save sExportpath & "\relations.rel.txt"
         Wscript.Echo "Relations successfuly saved in file relations.rel.txt"
        End If
    
        oApplication.CloseCurrentDatabase
        oApplication.Quit
    
    End Function
    

    You can execute this script by calling cscript decompose.vbs <path to file to decompose> <folder to store text files>. In case you omit the second parameter, it will create 'Source' folder where the database is located. Please note that destination folder will be wiped if it already exists.

    Include data in the exported tables

    Replace line 93: oApplication.ExportXML acExportTable, myObj.Name, , sExportpath & "\" & myObj.Name & ".table.txt"

    with line oApplication.ExportXML acExportTable, myObj.Name, sExportpath & "\" & myObj.Name & ".table.txt"

    Import into Create database file (compose.vbs)

    ' Usage:
    '  cscript compose.vbs <file> <path>
    
    ' Reads all modules, classes, forms, macros, queries, tables and their relationships in a directory created by "decompose.vbs"
    ' and composes then into an Access Database file (.accdb).
    ' Requires Microsoft Access.
    Option Explicit
    
    Const acForm = 2
    Const acModule = 5
    Const acMacro = 4
    Const acReport = 3
    Const acQuery = 1
    Const acStructureOnly = 0   'change 0 to 1 if you want import StructureAndData instead of StructureOnly
    Const acCmdCompileAndSaveAllModules = &H7E
    
    Dim fso, relDoc, ACCDBFilename, sPath
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set relDoc = CreateObject("Microsoft.XMLDOM")
    
    If (Wscript.Arguments.Count = 0) Then
     MsgBox "Please provide the .accdb database file", vbExclamation, "Error"
     Wscript.Quit()
    End If
    
    ACCDBFilename = fso.GetAbsolutePathName(Wscript.Arguments(0))
    If (Wscript.Arguments.Count = 1) Then
     sPath = ""
    Else
     sPath = Wscript.Arguments(1)
    End If
    
    
    importModulesTxt ACCDBFilename, sPath
    
    If (Err <> 0) And (Err.Description <> Null) Then
        MsgBox Err.Description, vbExclamation, "Error"
        Err.Clear
    End If
    
    
    Function importModulesTxt(ACCDBFilename, sImportpath)
        Dim myComponent, sModuleType, sTempname, sOutstring
    
        ' Build file and pathnames
        Dim myType, myName, myPath
        myType = fso.GetExtensionName(ACCDBFilename)
        myName = fso.GetBaseName(ACCDBFilename)
        myPath = fso.GetParentFolderName(ACCDBFilename)
    
        ' if no path was given as argument, use a relative directory
        If (sImportpath = "") Then
            sImportpath = myPath & "\Source\"
        End If
    
        ' check for existing file and ask to overwrite with the stub
        If fso.FileExists(ACCDBFilename) Then
         Wscript.StdOut.Write ACCDBFilename & " already exists. Overwrite? (y/n) "
         Dim sInput
         sInput = Wscript.StdIn.Read(1)
         If (sInput <> "y") Then
          Wscript.Quit
         Else
          If fso.FileExists(ACCDBFilename & ".bak") Then
           fso.DeleteFile (ACCDBFilename & ".bak")
          End If
          fso.MoveFile ACCDBFilename, ACCDBFilename & ".bak"
         End If
        End If
    
        Wscript.Echo "starting Access..."
        Dim oApplication
        Set oApplication = CreateObject("Access.Application")
        Wscript.Echo "Opening " & ACCDBFilename
        If (Right(ACCDBFilename, 4) = ".adp") Then
            oApplication.CreateAccessProject ACCDBFilename
        Else
            oApplication.NewCurrentDatabase ACCDBFilename
        End If
        oApplication.Visible = False
    
        Dim folder
        Set folder = fso.GetFolder(sImportpath)
    
        'load each file from the import path into the stub
        Dim myFile, objectname, objecttype
        For Each myFile In folder.Files
         objectname = fso.GetBaseName(myFile.Name)  'get rid of .txt extension
         objecttype = fso.GetExtensionName(objectname)
         objectname = fso.GetBaseName(objectname)
    
         Select Case objecttype
          Case "form"
           Wscript.Echo "Importing FORM from file " & myFile.Name
           oApplication.LoadFromText acForm, objectname, myFile.Path
          Case "module"
           Wscript.Echo "Importing MODULE from file " & myFile.Name
           oApplication.LoadFromText acModule, objectname, myFile.Path
          Case "macro"
           Wscript.Echo "Importing MACRO from file " & myFile.Name
           oApplication.LoadFromText acMacro, objectname, myFile.Path
          Case "report"
           Wscript.Echo "Importing REPORT from file " & myFile.Name
           oApplication.LoadFromText acReport, objectname, myFile.Path
          Case "query"
           Wscript.Echo "Importing QUERY from file " & myFile.Name
           oApplication.LoadFromText acQuery, objectname, myFile.Path
          Case "table"
           Wscript.Echo "Importing TABLE from file " & myFile.Name
           oApplication.ImportXml myFile.Path, acStructureOnly
          Case "rel"
           Wscript.Echo "Found RELATIONSHIPS file " & myFile.Name & " ... opening, it will be processed after everything else has been imported"
           relDoc.Load (myFile.Path)
         End Select
        Next
    
        If relDoc.readyState Then
         Wscript.Echo "Preparing to build table dependencies..."
         Dim xmlRel, xmlField, accessRel, relTable, relName, relFTable, relAttr, i
         For Each xmlRel In relDoc.SelectNodes("/Relations/Relation")   'loop through every Relation node inside .xml file
          relName = xmlRel.SelectSingleNode("Name").Text
          relTable = xmlRel.SelectSingleNode("Table").Text
          relFTable = xmlRel.SelectSingleNode("ForeignTable").Text
          relAttr = xmlRel.SelectSingleNode("Attributes").Text
    
          'remove any possible conflicting relations or indexes
          On Error Resume Next
          oApplication.CurrentDb.Relations.Delete (relName)
          oApplication.CurrentDb.TableDefs(relTable).Indexes.Delete (relName)
          oApplication.CurrentDb.TableDefs(relFTable).Indexes.Delete (relName)
          On Error GoTo 0
    
          Wscript.Echo "Creating relation " & relName & " between tables " & relTable & " -> " & relFTable
          Set accessRel = oApplication.CurrentDb.CreateRelation(relName, relTable, relFTable, relAttr)  'create the relationship object
    
          For Each xmlField In xmlRel.SelectNodes("Field")  'in case the relationship works with more fields
           accessRel.Fields.Append accessRel.CreateField(xmlField.SelectSingleNode("Name").Text)
           accessRel.Fields(xmlField.SelectSingleNode("Name").Text).ForeignName = xmlField.SelectSingleNode("ForeignName").Text
           Wscript.Echo "  Involving fields " & xmlField.SelectSingleNode("Name").Text & " -> " & xmlField.SelectSingleNode("ForeignName").Text
          Next
    
          oApplication.CurrentDb.Relations.Append accessRel 'append the newly created relationship to the database
          Wscript.Echo "  Relationship added"
         Next
        End If
    
        oApplication.RunCommand acCmdCompileAndSaveAllModules
        oApplication.Quit
    End Function
    

    You can execute this script by calling cscript compose.vbs <path to file which should be created> <folder with text files>. In case you omit the second parameter, it will look into 'Source' folder where the database should be created.

    Import data from text file

    Replace line 14: const acStructureOnly = 0 with const acStructureOnly = 1. This will work only if you have included the data in exported table.

    Things that are not covered

    1. I have tested this only with .accdb files, so with anything else there might be some bugs.
    2. Setting are not exported, I would recommend creating the Macro that will apply the setting at start of the database.
    3. Some unknown queries sometimes get exported that are preceded with '~'. I don't know if they are necessary.
    4. MSAccess object names can contain characters that are invalid for filenames - the script will fail when trying to write them. You may normalize all filenames, but then you cannot import them back.

    One of my other resources while working on this script was this answer, which helped me to figure out how to export relationships.

    0 讨论(0)
  • 2020-11-22 13:07

    I found this tool on SourceForge: http://sourceforge.net/projects/avc/

    I haven't used it, but it may be a start for you. There may be some other 3rd party tools that integrate with VSS or SVN that do what you need.

    Personally I just keep a plain text file handy to keep a change log. When I commit the binary MDB, I use the entries in the change log as my commit comment.

    0 讨论(0)
提交回复
热议问题