当前位置:  开发笔记 > 编程语言 > 正文

如何在Access开发中使用版本控制?

如何解决《如何在Access开发中使用版本控制?》经验,为你挑选了7个好方法。

我参与了更新Access解决方案.它具有大量的VBA,大量查询,少量表格以及一些用于数据输入和报告生成的表单.它是Access的理想候选者.

我想对表设计,VBA,查询和表单进行更改.如何通过版本控制跟踪我的更改?(我们使用Subversion,但这适用于任何风格)我可以将整个mdb粘贴在subversion中,但这将存储二进制文件,我将无法告诉我只更改了一行VBA代码.

我想过将VBA代码复制到单独的文件中并保存它们,但我可以看到它们很快就与数据库中的内容不同步了.



1> Oliver..:

我们在VBScript中编写了自己的脚本,它使用Access中未记录的Application.SaveAsText()来导出所有代码,表单,宏和报表模块.在这里,它应该给你一些指示.(注意:有些消息是德语的,但你可以很容易地改变它.)

编辑:总结以下各种评论: 我们的项目假设一个.adp文件.为了使用.mdb/.accdb工作,您必须将OpenAccessProject()更改为OpenCurrentDatabase().(OpenAccessProject()如果它看到.adp扩展名,则更新使用,否则使用OpenCurrentDatabase().)

decompose.vbs:

' Usage:
'  CScript decompose.vbs  

' Converts all modules, classes, forms and macros from an Access Project file (.adp)  to
' text and saves the results in separate files to .  Requires Microsoft Access.
'

Option Explicit

const acForm = 2
const acModule = 5
const acMacro = 4
const acReport = 3

' BEGIN CODE
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")

dim sADPFilename
If (WScript.Arguments.Count = 0) then
    MsgBox "Bitte den Dateinamen angeben!", vbExclamation, "Error"
    Wscript.Quit()
End if
sADPFilename = fso.GetAbsolutePathName(WScript.Arguments(0))

Dim sExportpath
If (WScript.Arguments.Count = 1) then
    sExportpath = ""
else
    sExportpath = WScript.Arguments(1)
End If


exportModulesTxt sADPFilename, sExportpath

If (Err <> 0) and (Err.Description <> NULL) Then
    MsgBox Err.Description, vbExclamation, "Error"
    Err.Clear
End If

Function exportModulesTxt(sADPFilename, sExportpath)
    Dim myComponent
    Dim sModuleType
    Dim sTempname
    Dim sOutstring

    dim myType, myName, myPath, sStubADPFilename
    myType = fso.GetExtensionName(sADPFilename)
    myName = fso.GetBaseName(sADPFilename)
    myPath = fso.GetParentFolderName(sADPFilename)

    If (sExportpath = "") then
        sExportpath = myPath & "\Source\"
    End If
    sStubADPFilename = sExportpath & myName & "_stub." & myType

    WScript.Echo "copy stub to " & sStubADPFilename & "..."
    On Error Resume Next
        fso.CreateFolder(sExportpath)
    On Error Goto 0
    fso.CopyFile sADPFilename, sStubADPFilename

    WScript.Echo "starting Access..."
    Dim oApplication
    Set oApplication = CreateObject("Access.Application")
    WScript.Echo "opening " & sStubADPFilename & " ..."
    If (Right(sStubADPFilename,4) = ".adp") Then
        oApplication.OpenAccessProject sStubADPFilename
    Else
        oApplication.OpenCurrentDatabase sStubADPFilename
    End If

    oApplication.Visible = false

    dim dctDelete
    Set dctDelete = CreateObject("Scripting.Dictionary")
    WScript.Echo "exporting..."
    Dim myObj
    For Each myObj In oApplication.CurrentProject.AllForms
        WScript.Echo "  " & myObj.fullname
        oApplication.SaveAsText acForm, myObj.fullname, sExportpath & "\" & myObj.fullname & ".form"
        oApplication.DoCmd.Close acForm, myObj.fullname
        dctDelete.Add "FO" & myObj.fullname, acForm
    Next
    For Each myObj In oApplication.CurrentProject.AllModules
        WScript.Echo "  " & myObj.fullname
        oApplication.SaveAsText acModule, myObj.fullname, sExportpath & "\" & myObj.fullname & ".bas"
        dctDelete.Add "MO" & myObj.fullname, acModule
    Next
    For Each myObj In oApplication.CurrentProject.AllMacros
        WScript.Echo "  " & myObj.fullname
        oApplication.SaveAsText acMacro, myObj.fullname, sExportpath & "\" & myObj.fullname & ".mac"
        dctDelete.Add "MA" & myObj.fullname, acMacro
    Next
    For Each myObj In oApplication.CurrentProject.AllReports
        WScript.Echo "  " & myObj.fullname
        oApplication.SaveAsText acReport, myObj.fullname, sExportpath & "\" & myObj.fullname & ".report"
        dctDelete.Add "RE" & myObj.fullname, acReport
    Next

    WScript.Echo "deleting..."
    dim sObjectname
    For Each sObjectname In dctDelete
        WScript.Echo "  " & Mid(sObjectname, 3)
        oApplication.DoCmd.DeleteObject dctDelete(sObjectname), Mid(sObjectname, 3)
    Next

    oApplication.CloseCurrentDatabase
    oApplication.CompactRepair sStubADPFilename, sStubADPFilename & "_"
    oApplication.Quit

    fso.CopyFile sStubADPFilename & "_", sStubADPFilename
    fso.DeleteFile sStubADPFilename & "_"


End Function

Public Function getErr()
    Dim strError
    strError = vbCrLf & "----------------------------------------------------------------------------------------------------------------------------------------" & vbCrLf & _
               "From " & Err.source & ":" & vbCrLf & _
               "    Description: " & Err.Description & vbCrLf & _
               "    Code: " & Err.Number & vbCrLf
    getErr = strError
End Function

如果需要可单击的命令,而不是使用命令行,请创建名为"decompose.cmd"的文件

cscript decompose.vbs youraccessapplication.adp

默认情况下,所有导出的文件都会进入Access-application的"Scripts"子文件夹..adp/mdb文件也会复制到此位置(带有"stub"后缀)并删除所有导出的模块,使其非常小.

您必须使用源文件签入此存根,因为大多数访问设置和自定义菜单栏无法以任何其他方式导出.如果您确实更改了某些设置或菜单,请确保仅对此文件提交更改.

注意:如果您的应用程序中定义了任何Autoexec-Makros,则在调用分解时可能必须按住Shift键以防止它执行并干扰导出!

当然,还有反向脚本,从"源" - 目录构建应用程序:

compose.vbs:

' Usage:
'  WScript compose.vbs  

' Converts all modules, classes, forms and macros in a directory created by "decompose.vbs"
' and composes then into an Access Project file (.adp). This overwrites any existing Modules with the
' same names without warning!!!
' Requires Microsoft Access.

Option Explicit

const acForm = 2
const acModule = 5
const acMacro = 4
const acReport = 3

Const acCmdCompileAndSaveAllModules = &H7E

' BEGIN CODE
Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")

dim sADPFilename
If (WScript.Arguments.Count = 0) then
    MsgBox "Please enter the file name!", vbExclamation, "Error"
    Wscript.Quit()
End if
sADPFilename = fso.GetAbsolutePathName(WScript.Arguments(0))

Dim sPath
If (WScript.Arguments.Count = 1) then
    sPath = ""
else
    sPath = WScript.Arguments(1)
End If


importModulesTxt sADPFilename, sPath

If (Err <> 0) and (Err.Description <> NULL) Then
    MsgBox Err.Description, vbExclamation, "Error"
    Err.Clear
End If

Function importModulesTxt(sADPFilename, sImportpath)
    Dim myComponent
    Dim sModuleType
    Dim sTempname
    Dim sOutstring

    ' Build file and pathnames
    dim myType, myName, myPath, sStubADPFilename
    myType = fso.GetExtensionName(sADPFilename)
    myName = fso.GetBaseName(sADPFilename)
    myPath = fso.GetParentFolderName(sADPFilename)

    ' if no path was given as argument, use a relative directory
    If (sImportpath = "") then
        sImportpath = myPath & "\Source\"
    End If
    sStubADPFilename = sImportpath & myName & "_stub." & myType

    ' check for existing file and ask to overwrite with the stub
    if (fso.FileExists(sADPFilename)) Then
        WScript.StdOut.Write sADPFilename & " exists. Overwrite? (y/n) "
        dim sInput
        sInput = WScript.StdIn.Read(1)
        if (sInput <> "y") Then
            WScript.Quit
        end if

        fso.CopyFile sADPFilename, sADPFilename & ".bak"
    end if

    fso.CopyFile sStubADPFilename, sADPFilename

    ' launch MSAccess
    WScript.Echo "starting Access..."
    Dim oApplication
    Set oApplication = CreateObject("Access.Application")
    WScript.Echo "opening " & sADPFilename & " ..."
    If (Right(sStubADPFilename,4) = ".adp") Then
        oApplication.OpenAccessProject sADPFilename
    Else
        oApplication.OpenCurrentDatabase sADPFilename
    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
        objecttype = fso.GetExtensionName(myFile.Name)
        objectname = fso.GetBaseName(myFile.Name)
        WScript.Echo "  " & objectname & " (" & objecttype & ")"

        if (objecttype = "form") then
            oApplication.LoadFromText acForm, objectname, myFile.Path
        elseif (objecttype = "bas") then
            oApplication.LoadFromText acModule, objectname, myFile.Path
        elseif (objecttype = "mac") then
            oApplication.LoadFromText acMacro, objectname, myFile.Path
        elseif (objecttype = "report") then
            oApplication.LoadFromText acReport, objectname, myFile.Path
        end if

    next

    oApplication.RunCommand acCmdCompileAndSaveAllModules
    oApplication.Quit
End Function

Public Function getErr()
    Dim strError
    strError = vbCrLf & "----------------------------------------------------------------------------------------------------------------------------------------" & vbCrLf & _
               "From " & Err.source & ":" & vbCrLf & _
               "    Description: " & Err.Description & vbCrLf & _
               "    Code: " & Err.Number & vbCrLf
    getErr = strError
End Function

同样,这与伴侣"compose.cmd"一起包含:

cscript compose.vbs youraccessapplication.adp

如果您这样做,它会要求您确认覆盖当前的应用程序并首先创建备份.然后,它会收集源目录中的所有源文件,并将它们重新插入到存根中.

玩得开心!


我希望我能不止一次投票给这个答案
是的,这也是我们项目中的一个持续烦恼.据我们所知,问题在于项目中的变量与控件具有相同的名称,只是在不同的情况下(向上/向下).现在,取决于正在组成的模块的顺序,Access似乎需要一个拼写并"纠正"所有其他拼写,因为VBA应该不区分大小写.访问是这样做的,即使控件是以不同的形式!如果在不同形式的不同情况下甚至有多个相同名称的控件,问题会变得更大.
唯一的解决方案是搜索每个变量/控件名称并将拼写更改为常用形式.导出并提交更改后,名称应该是稳定的.使用类型前缀控件名称几乎可以通过命名约定确保名称不会与变量冲突.(例如,包含Title字段的文本框的txtTitle或组合框的cmbUsers等)

2> Brettski..:

它似乎是Access中非常有用的东西:

msdn的此链接说明了如何为Microsoft Access安装源代码管理加载项.它作为Access Developer Extensions for Access 2007的一部分免费下载,并作为Access 2003的单独免费插件提供.

我很高兴你问这个问题,我花时间去查阅,因为我也想要这个能力.上面的链接提供了有关此内容的更多信息以及指向加载项的链接.

更新:
我安装了Access 2003的加载项.它只能用于VSS,但它允许我将Access对象(表单,查询,表,模块等)放入存储库.当您编辑回购中的任何项目时,系统会要求您检查它,但您不必这样做.接下来,我将检查它如何处理在没有加载项的系统上打开和更改.我不是VSS的粉丝,但我确实喜欢在存储库中存储访问对象的想法.

Update2:
没有加载项的计算机无法对数据库结构进行任何更改(添加表字段,查询参数等).起初我认为如果有人需要这可能是一个问题,因为如果Access没有加载加载项,没有明显的方法从源代码控制中删除Access数据库.

我发现运行"compact and repair"数据库会提示您是否要从源代码管理中删除数据库.我选择了是,并且能够在没有加载项的情况下编辑数据库.上面链接中的文章还说明了如何设置Access 2003和2007以使用Team System.如果您能找到SVN的MSSCCI提供程序,那么很有可能您可以使用它.



3> DaveParillo..:

Olivers回答岩石,但CurrentProject参考不适合我.基于Arvin Meyer的类似解决方案,我最终从他的出口中间撕下了胆量并用它替换了它.如果您使用的是mdb而不是adp,则具有导出查询的优点.

' Writes database componenets to a series of text files
' @author  Arvin Meyer
' @date    June 02, 1999
Function DocDatabase(oApp)
    Dim dbs 
    Dim cnt 
    Dim doc 
    Dim i
    Dim prefix
    Dim dctDelete
    Dim docName

    Const acQuery = 1

    Set dctDelete = CreateObject("Scripting.Dictionary")

    Set dbs = oApp.CurrentDb() ' use CurrentDb() to refresh Collections
    Set cnt = dbs.Containers("Forms")
    prefix = oApp.CurrentProject.Path & "\"
    For Each doc In cnt.Documents
        oApp.SaveAsText acForm, doc.Name, prefix & doc.Name & ".frm"
        dctDelete.Add "frm_" & doc.Name, acForm
    Next

    Set cnt = dbs.Containers("Reports")
    For Each doc In cnt.Documents
        oApp.SaveAsText acReport, doc.Name, prefix & doc.Name & ".rpt"
        dctDelete.Add "rpt_" & doc.Name, acReport
    Next

    Set cnt = dbs.Containers("Scripts")
    For Each doc In cnt.Documents
        oApp.SaveAsText acMacro, doc.Name, prefix & doc.Name & ".vbs"
        dctDelete.Add "vbs_" & doc.Name, acMacro
    Next

    Set cnt = dbs.Containers("Modules")
    For Each doc In cnt.Documents
        oApp.SaveAsText acModule, doc.Name, prefix & doc.Name & ".bas"
        dctDelete.Add "bas_" & doc.Name, acModule
    Next

    For i = 0 To dbs.QueryDefs.Count - 1
        oApp.SaveAsText acQuery, dbs.QueryDefs(i).Name, prefix & dbs.QueryDefs(i).Name & ".txt"
        dctDelete.Add "qry_" & dbs.QueryDefs(i).Name, acQuery
    Next

    WScript.Echo "deleting " & dctDelete.Count & " objects."
    For Each docName In dctDelete
        WScript.Echo "  " & Mid(docName, 5)
        oApp.DoCmd.DeleteObject dctDelete(docName), Mid(docName, 5)
    Next

    Set doc = Nothing
    Set cnt = Nothing
    Set dbs = Nothing
    Set dctDelete = Nothing

End Function


我强烈建议在保存表单之前保存查询,以便稍后更改删除顺序.当我尝试删除之前已删除相应表单时已自动删除的查询时,我在上一个For Each语句中遇到了DeleteObject的问题.此外,如果您在启动时打开某些表单并且不想保留F11(或将其取消激活),只需在运行crou.Documents后插入oApp.DoCmd.Close acForm,"formName".

4> hansfn..:

Oliver发布的撰写/分解解决方案很棒,但它存在一些问题:

这些文件被编码为UCS-2(UTF-16),这可能导致版本控制系统/工具将文件视为二进制文件.

这些文件包含许多经常更改的错误 - 校验和,打印机信息等.如果您想要干净的差异或需要在项目上合作,这是一个严重的问题.

我打算自己解决这个问题,但发现已经有一个很好的解决方案:在GitHub上进行timabell/msaccess-vcs-integration.我已经测试了msaccess-vcs-integration,它确实很有效.

2015年3月3日更新:该项目最初由bkidwell在Github上维护/拥有,但它被转移到timabell - 上面链接到项目相应更新.bkidwell的原始项目中有一些分叉,例如ArminBra和matonb,不应使用AFAICT.

与Olivers的分解解决方案相比,使用msaccess-vcs-integration的缺点是:

它明显变慢了.我确定速度问题可以修复,但我不需要将我的项目导出到经常出现的文本...

它不会创建存根Access项目,并删除导出的东西.这也可以修复(通过采用分解脚本中的代码),但同样重要 - 不那么重要.

无论如何,我的明确建议是msaccess-vcs-integration.它解决了我在导出文件上使用Git时遇到的所有问题.


@TimAbell:我已经更新了我的答案,以反映项目已转移给您的事实.PS!我希望我们可以得到一些投票,因为我认为这是最好的解决方案.
好的,导航github项目的分叉似乎是我们为自己发明的最新问题:-)

5> Philippe Gro..:

我们开发了自己的内部工具,其中:

    模块:导出为txt文件,然后与"文件比较工具"(免费软件)进行比较

    表单:通过undocument application.saveAsText命令导出.然后可以看到2个不同版本之间的差异(再次"文件比较工具").

    宏:我们没有任何宏可以比较,因为我们只有"autoexec"宏,一行启动主VBA程序

    查询:只是存储在表中的文本字符串:见下文

    tables:我们编写了自己的表比较器,列出了记录和表结构的差异.

整个系统非常智能,允许我们生成Access应用程序的"运行时"版本,这些版本是从txt文件(模块和使用undocument application.loadFromText命令重新创建的表单)和mdb文件(表)自动生成的.

听起来可能很奇怪,但它确实有效.


很想看到这个工具开源!

6> mnieto..:

根据这篇文章的想法和一些博客中的类似条目,我写了一个适用于mdb和adp文件格式的应用程序.它将所有数据库对象(包括表,引用,关系和数据库属性)导入/导出到纯文本文件.使用这些文件,您可以使用任何源版本控件.下一版本将允许将纯文本文件导回数据库.还将有一个命令行工具

您可以从以下网址下载应用程序或源代码:http://accesssvn.codeplex.com/

问候



7> JKK..:

复活一个旧线程,但这是一个很好的.我已经为我自己的项目实现了两个脚本(compose.vbs/decompose.vbs),并遇到了旧的.mdb文件的问题:

当它到达包含代码的表单时,它会停止:

NoSaveCTIWhenDisabled =1

Access说它有一个问题,这就是故事的结局.我运行了一些测试并试图解决这个问题并发现这个线程在最后解决了:

无法创建数据库

基本上(如果线程死机),你获取.mdb并对新的.accdb格式执行"另存为".然后源安全或撰写/分解的东西将起作用.我还必须玩10分钟才能获得正确的命令行语法(de)compose脚本才能正常工作,所以这里的信息也是如此:

编写(比如你的东西位于C:\ SControl中(创建一个名为Source的子文件夹来存储提取的文件):

'(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\

而已!

我遇到上述问题的Access版本包括Access 2000-2003".mdb"数据库,并通过在运行compose/decompose脚本之前将它们保存为2007-2010".accdb"格式来解决问题.转换后,脚本工作得很好!

推荐阅读
pan2502851807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有