Manual Table Relinking

Friday Mar 17th 2006 by Danny Lesandrini

Last month's article demonstrated how to implement table linking, with no user interaction required. At times you need to allow for manual table relinking. This article consolidates some of the tools and tricks that the author has used over the years to serve as a starting point for your solution.

Last month's article, Automatic Table relinking, demonstrated how to implement table linking behind the scenes, with no user interaction required.  However, there are times when we have to trust the users with the awesome responsibility of pointing to the correct database.  Perhaps it is only at the initial setup of the client application, or perhaps your program allows users to connect to different databases.  Whatever the reason, at times you need to allow for manual table relinking.

Once again, I am compelled to point out that I have seen many and various solutions to this issue, and any number of them may be used with success.  The point of this article is not to show the best way to provide a table linking paradigm but rather to consolidate some of the tools and tricks that I have used over the years to serve as a starting point for your solution. 

The download for this article includes a module named API_Functions , which includes published code from some of the all-time great Access programmers, like Dev Ashish and Terry Kreft.  I have been using this code for years (nearly a decade, now that I think about it) and these code snippets always include an attribution to give credit where credit is due.  The rest of the demo, its forms and module code, is my own.  Feel free to use it in part or in whole, but please retain references to the generous programmers who contributed these valuable VBA functions.

The objective for this article is to demonstrate the following:

1.  Relink ODBC tables to Pubs database on a SQL Server 

2.  Demonstrate how to display a Progress meter with cancel button  

3.  Show the same process for dynamic linking to an MDB file

4.  How to open the Linked Table Manager wizard. 

To give you an idea of what this demo does, consider the screen shot below.  The main form reads the list of linked objects from the MsysObjects table and gives you three options for relinking:  link only the ODBC to SQL Server tables, Link only the MDB tables or open the Linked Table Manager wizard. The first two options prompt you for a source (DSN for the ODBC request and File Path for the MDB option).  Once selected, the relinking process begins and the progress meter displays the process metadata.  Clicking the Cancel button aborts the process.  Cool, huh?  Now let's see how it is done.

Click for larger image

Attach ODBC Method

The process for relinking to an ODBC data source, such as SQL Server Pubs database, requires the following steps:

  • Get the connection string
  • Loop through the correct set of tables
  • Set the tabledef's connect property
  • Refresh the link

Now, the most difficult part of this is finding the ODBC connect string. One could hard code a list of Servers and Databases and allow the user to select from the list, but a more flexible solution is to allow the user to link by means of new or preexisting ODBC DSNs.  However, how does one open the ODBC Data Source Administrator dialog box?  Well, here's a little trick for that:

   ' This is a relatively simple way to capture a connect string.
   ' Create a database objects and open an ODBC connection.
   ' Then simply read the connect string and discard the object.
   Set wks = DBEngine.Workspaces(0)
   Set dbs = wks.OpenDatabase("", False, False, "ODBC;")
   m_strConnect = dbs.Connect
   GetDSN = True
   ' The results of this call is to set the module level variable, m_strConnect,
   ' to the selected connect string.  It will look something like this ...
   ODBC;DSN= My_Pubs_dsn;Trusted_Connection=Yes;DATABASE=pubs

Now, looping through tables to reset their connect string and refresh the link is a trivial matter. This is the same code we used in the last article, with the one distinction that the connect property is now being set to the user-selected value that was stored in the global variable named  m_strConnect.

   Dim dbs As DAO.Database
   Dim tdf As DAO.TableDef
   Set dbs = CurrentDb()
   ' Loop through TableDefs collection, only processing
   ' the table if it already has a Connection property.
   For Each tdf In dbs.TableDefs
      If tdf.Connect <> "" Then
         tdf.Connect = m_strConnect
      End If

That's all there is to it. We have exposed the ODBC Data Source Administrator dialog box and allowed the user to select a database. This is great for SQL Server tables, but it will not work if you need to connect to another Access database. Let's see how that might be accomplished.

Attach MDB Method

Warning: The following code works ONLY if you import the API_Functions module into your MDB file.

It is beyond the scope of this article to explain the inner-workings of the find-file common dialog box, but I will demonstrate how to use the methods that are included in the API_Functions module included in the download. You will have to play around with it a little, but if you haven't already discovered this little gem, you may find this to be the most valuable piece of code in the download.

  Dim strFileMDB As String
   Dim myOpenFile As gtypCw_OPENFILE
   Dim strExt As String
   Dim strFilter As String
   ' Specify the default extension
   With myOpenFile
      .strDefaultExtension = ".mdb"
      .strDialogTitle = "Locate MDB data File"
      .strFilter = CreateFilterString("PubsData.mdb", "*.mdb")
      .strInitialDir = CurrentProject.Path
      .strInitialFile = "PubsData.mdb"
      strFileMDB = GetOpenFileEX(myOpenFile)
      strFileMDB = .strFullPathReturned
   End With
   FindMDBFile = myOpenFile.strFullPathReturned

The above snippet is taken from a public function named   FindMDBFile()  which returns the path to the data file selected by the user. Once you have that, you are ready to relink to the MDB tables. The code for refreshing links is almost identical to that above, with one exception: You must prefix the file path with the text  ;DATABASE=   to indicate that you're linking to an Access MDB database.

   strDataFile = FindMDBFile()
   Set dbs = CurrentDb
   '   ;DATABASE=C:\Development\Crestone\DataCTG.mdb
   strConnect = ";DATABASE=" & strDataFile
   For Each tdf In dbs.TableDefs
      If tdf.Connect <> "" Then
         tdf.Connect = strConnect
      End If

To simplify the article I have posted only the code that does the actual work. Of course, you will need to declare all your variables and you will want to add error handling, especially in the case of table linking. If and when it fails, you will need to know because it is possible that such an error could preclude opening the application at all. Even in the best scenario, where a single table fails to relink, it is important for debugging to know and log such errors.

Display the Progress

The sample code includes a form named frmProgress, shown in the image below.  This form is manipulated by a public sub named UpdateProgressMeter, which updates the display by extending the length of a blue label to give a graphic representation of how far along the process is.  The lower label also accepts a new caption and the form is repainted.

   Public Sub UpdateProgressMeter(ByVal strMsg As String, _
                                  ByVal lngCurr As Long, _
                                  ByVal lngTotal As Long)
      On Error Resume Next
      Dim intPercent As Integer
      intPercent = (lngCurr / lngTotal) * 100
      With Forms!frmProgress
         !lblBlue.Caption = "Processing  " & intPercent & "%  Completed"
         !lblTransparent.Caption = "Processing  " & intPercent & "%  Completed"
         !lblBlue.Width = intPercent / 100 * !lblTransparent.Width
         !lblTable.Caption = strMsg
      End With
   End Sub

The process for implementing this progress meter is simple and the following steps are illustrated in the code snippet below:

  • Open form and initialize values
  • Call the Update method with each table processed
  • Watch for the Cancel flag and respond accordingly
  • Close the form when finished
   ' Count the total number of ODBC linked tables (Type=4)
   ' This will be used to determine how far along we are
   ' with the linking of each new table.
   lngTotal = DCount("*", "MsysObjects", "[Type]=4")
   ' Open the progress form and set its properties
   DoCmd.OpenForm "frmProgress"
   strMsg = "Ready to link to ODBC tables"
   UpdateProgressMeter strMsg, 0, lngTotal
   Set dbs = CurrentDb
   For Each tdf In dbs.TableDefs
      If tdf.Connect <> "" Then
         ' Update progress meter with info about current table
         lngCurr = lngCurr + 1
         strMsg = "Linking table " & tdf.Name
         UpdateProgressMeter strMsg, lngCurr, lngTotal
         ForcePause 1
         tdf.Connect = m_strConnect: tdf.RefreshLink
         ' If user pressed the Cancel button, then the global
         ' variable will be true.  In that case, exit the loop.
         If m_fCancel = True Then Exit For
      End If
   ' Clean up by closing the form.
   DoCmd.Close acForm, "frmProgress"

Not Exactly A Cut-And-Paste Solution

As with most of my articles, the code is supplied as a framework, not a cut-and-paste solution.  Your table linking schema is probably going to be more complicated than what is shown above.  For that matter, the sample code in the download file is more complicated than what is shown above, since I have to manage two sets of tables:  ODBC and MDB linked.  The test for tdf.Connect <> "" isn't sufficient to distinguish which are linked to SQL Server and which to Access.  However, these matters are usually trivial and business rules easy to implement, once you have the basic idea.

I hope you found this discussion useful.  Download the code and play with it.  The Progress Meter and the Find File code are worth it, even if you aren't currently looking for a table linking solution.  As usual, I welcome your feedback, comments and questions.

» See All Articles by Columnist Danny J. Lesandrini

Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved