Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 115 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
115
Dung lượng
1,35 MB
Nội dung
Let’s say that in the issue tracking example that you would like to see the data sorted in descending order. The following code uses the ColumnHistory method in Access to retrieve the values that were in the column and add them to a list box named lstHistory: Private Sub ShowColumnHistory(strTableName As String, strFieldName As String) ‘History data is in this format: ‘[Version: Date Time ] History Data Const VERSION_PREFIX As String = “[Version: “ Dim strHistory As String Dim strHistoryItem As String Dim astrHistory() As String Dim lngCounter As Long Dim datDate As Date Dim datTime As Date Dim strData As String ‘Get the column history strHistory = Application.ColumnHistory(strTableName, strFieldName, “”) ‘Make sure there is history data If Len(strHistory) > 0 Then ‘Parse the column history into separate items. ‘Each item in the history is separated by a vbCrLf, but ‘if there are carriage-returns in the memo field data ‘you will get unexpected results. Split on the VERSION string ‘in the history data. astrHistory = Split(strHistory, VERSION_PREFIX) ‘Adding these lines ensures this code works regardless of ‘how the control is configured on the form Me.lstHistory.RowSourceType = “Value List” Me.lstHistory.ColumnCount = 3 Me.lstHistory.ColumnHeads = True ‘Add column headings to the list box Me.lstHistory.AddItem “Date;Time;History” ‘Enumerate the history data in reverse ‘to fill the list box in descending order For lngCounter = UBound(astrHistory) To LBound(astrHistory) Step -1 ‘Parse the history data strHistoryItem = astrHistory(lngCounter) If Len(strHistoryItem) > 0 Then ‘Parse the date from the history data. ‘This example parse the default US date format. datDate = CDate(Left(strHistoryItem, InStr(strHistoryItem, “ “) - 1)) strHistoryItem = Mid(strHistoryItem, InStr(strHistoryItem, “ “) + 1) ‘Parse the time from the history data datTime = CDate(Left(strHistoryItem, InStr(strHistoryItem, “ ] “) - 1)) strHistoryItem = Mid(strHistoryItem, InStr(strHistoryItem, “ ] “) + 3) 188 Chapter 6: Using DAO to Access Data 47033c06.qxd:WroxProgRef 3/30/07 12:25 AM Page 188 ‘Add the history item to the list box. Me.lstHistory.AddItem datDate & “;” & datTime & “;” & strHistoryItem End If Next Else MsgBox “There is no history information for the specified field” End If End Sub The form with the list box is shown in Figure 6-10. Figure 6-10 Summary In this chapter, you took a detailed look into the world of DAO including powerful new features that have been added in Access 2007. By now you should have a fairly good understanding of when to use DAO, and how to refer to its objects. You should have a good working knowledge of the main objects in the hierarchy, such as the DBEngine, Workspace, Error, Database, and Recordset objects, and their associated collections. Although very few people can remember every single property and method of every object, you should have gained enough exposure to them by now, to be able to start writing some reasonably sophisticated software. In any case, IntelliSense can help you out when you’re unsure. Now that you’ve mastered DAO, you’re ready to tackle a whole new object model — ActiveX Data Objects. 189 Chapter 6: Using DAO to Access Data 47033c06.qxd:WroxProgRef 3/30/07 12:25 AM Page 189 47033c06.qxd:WroxProgRef 3/30/07 12:25 AM Page 190 Using ADO to Access Data Data Access Objects (DAO) was the default data access technology in the early versions of Access. In fact, Access was bound so closely to the Jet database engine by the fact that developers used Access as both the front-end user interface and the back-end data store that they rarely felt a need for any- thing else. As application designs evolved from standalone solutions into client/server architectures, the need to connect to and access data from disparate data sources became more and more impor- tant. Although Microsoft made several attempts at evolving DAO into a remote data access technol- ogy, its true strength is accessing data from local Jet databases. So to fulfill the need to connect to external data sources, Microsoft created ActiveX Data Objects (ADO). ADO is designed to provide an ActiveX standard object model for connecting to a wide variety of external data sources. ADO is a part of Microsoft’s data access vision of the future, called Universal Data Access (UDA). UDA is a concept in which a single method is used to retrieve data from any data source: relational databases, the mainframe indexed sequential access method/virtual storage access method (ISAM/VSAM) data sources, hierarchical databases, e-mail, disk files, graphical data, and so on. OLE DB (Object Linking and Embedding Databases) is the interface that enables UDA. ADO is a development interface for OLE DB that provides similar functionality to DAO. OLE DB sees the world in terms of data providers. It acts as an interface between the data source and data consumer. Although OLE DB was written for procedural programming models, ADO sits atop OLE DB, providing programmers with an object-oriented model they can use to access and manipulate the data sources. When you use Access 2007 to create a standard Jet database (MDB) or standard ACE database (ACCDB), by default, Access uses the ACE OLE DB provider for the connection to the CurrentProject. Any functionality that uses the CurrentProject object will go through the OLE DB provider. To confirm this, enter the following line of code into the Visual Basic Editor’s Immediate window: ?CurrentProject.Connection The return connection string begins with the following: Provider=Microsoft.ACE.OLEDB.12.0; 47033c07.qxd:WroxProgRef 3/30/07 12:25 AM Page 191 Similarly, Access uses the Access OLE DB provider when you create an Access Data Project (ADP) against the SQL Server or MSDE. The same property call returns a connection string that begins thus: Provider=Microsoft.Access.OLEDB.10.0; While ADPs use the Access OLE DB provider for the CurrentProject object, the data provider for an ADP is the SQLOLEDB provider because an ADP is connected to a SQL data source. This chapter explores ADO and how it applies to an Access database solutions. Ambiguous References In versions prior to Access 2000, the DAO object library reference was selected by default for new data- base solutions. This enabled you to write code using DAO without requiring you to realize that your code depended on this object model. Starting in Access 2000, the ADO object library was referenced by default for new VBA projects (expect- ing you to use the ADO library for accessing data). Unfortunately, the change generated much confu- sion. It turned out that average Access developers didn’t know the difference between the two object models. Many developers learned the hard way when their applications started generating errors in code that had previously worked. By the time Microsoft realized the ramifications of adding ADO as the default reference, Access 2002 was already in beta, and the ADO reference remained. The issue was finally resolved in Access 2003 by refer- encing both object libraries by default. In Access 2007, there is another change to default references in databases: ❑ ACCDB files have a reference to ACE DAO. ❑ MDB files have a reference to DAO 3.6. Of course, ADP files only have a reference to ADO 2.5 because DAO does not apply to SQL data sources. Writing unambiguous VBA code has never been more important. There are likely thousands of Access applications that use code that does not reference a specific library when multiple references are present. If this code is run in a different version of Access than the version in which the database was created, you may encounter errors where code had previously worked. Consider the following code: Dim db As Database Dim rs As Recordset Nothing is terribly strange in this code, but there are two things to consider: First, only DAO has a Database object — ADO has none. Second, DAO and ADO both have Recordset objects. This begs the question: If both DAO and ADO object libraries are selected, to which library does the Recordset object refer? If you have only one library referenced, Access chooses the only possible Recordset object. However, if you have references to both ADO and DAO, Access examines the library list and chooses the first possi- ble reference for the object that it finds. If the reference ordering becomes switched, the database solution may begin behaving in strange ways, causing failures. 192 Chapter 7: Using ADO to Access Data 47033c07.qxd:WroxProgRef 3/30/07 12:25 AM Page 192 Many professional Access developers have written code using DAO and ADO objects interchangeably, and could not understand why the code failed to work. To ensure that Access (and you) knows which object refers to which object library, explicitly specify the object library. The following code illustrates explicit references to ADO and DAO objects: Dim db As DAO.Database Dim cn As ADODB.Connection Dim rsDAO As DAO.Recordset Dim rsADO As ADODB.Recordset Clarifying references also makes the code run a tad faster because Access doesn’t have to examine the library list to decide which library to use. Referring to ADO Objects When you refer to ADO objects, you do so in the same way you would using DAO. The difference is that ADO does not have a native data connection like DAO in ACCDB and MDB databases. Recall that in DAO you can use the DBEngine(0)(0) object or the CurrentDb() function to return a reference to the current database. ADO does not have a current database. In fact, it doesn’t know any- thing about datasets in the data source; it only knows about data source through the provider. In ADO, you must always implement a connection to an external data store through a provider before any actions can be executed for the data. As in DAO, an object’s parent collection can have a default member to which you can refer. The follow- ing table lists the default members for those ADO objects that have them. Object Library Collection Default Member Example ADODB Command Parameters cmd(0) Record Fields rcADO(0) Recordset Fields rsADO(0) ADOX Catalog Tables cat(0) Table Columns cat.tables(0)(0) Connecting to a Data Source The Connection object is considered the top-level object in the ADO object model. Although it doesn’t contain all the other ADO objects, as the DAO DBEngine object does, you must specify the connection that the other ADO objects will use to carry out their functions. The Connection object represents a single connection to an OLE DB provider, but of course you can cre- ate several connection objects, each connecting to a different provider. There are two ways to create a connection: implicitly and explicitly. 193 Chapter 7: Using ADO to Access Data 47033c07.qxd:WroxProgRef 3/30/07 12:25 AM Page 193 To create an implicit connection, you supply the connection string when creating a child object, such as a Recordset. The following example opens the connection implicitly when opening a Recordset: Function OpenRecordsetADO() As ADODB.Recordset ‘ Define Variables Dim strSQL As String Dim rs As New ADODB.Recordset ‘ Set the SQL for the Recordset strSQL = “SELECT [CONTACTS].* FROM [CONTACTS];” ‘ Open the Recordset using to Contacts connection rs.Open strSQL, CurrentProject.Connection Set CreateConnectionADO = rs End Function This creates a temporary connection that is destroyed when you close the Recordset object. To create an explicit connection, you must declare it, instantiate it, supply it with the various properties it needs, and then open it. The following function creates a connection to the current database and returns it to the caller: Function CreateConnectionADO() As ADODB.Connection ‘ Define Variables Dim strConnectionString As String Dim cn As New ADODB.Connection ‘ Set the Connection Settings With cn .ConnectionString = CurrentProject.Connection .CursorLocation = adUseClient .Attributes = .Attributes Or adXactCommitRetaining End With ‘ Open the Connection cn.Open Set CreateConnectionADO = cn End Function This example uses the CurrentProject.Connection property to supply the connection string infor- mation for the connection. In DAO, you would have used DBEngine(0)(0) or CurrentDb(). Because ADO does not have a Database object, use the CurrentProject object instead. The Open method for the Connection will fail to open an Access database if it is already opened in exclusive mode by another source, such as the current instance of Access that is running the code. This could easily be the case with the previous code sample because it creates the new connection with CurrentProject.Connection. 194 Chapter 7: Using ADO to Access Data 47033c07.qxd:WroxProgRef 3/30/07 12:25 AM Page 194 You generally create an explicit connection when connecting to an external data source from an ACCDB, MDB, or ADP. CurrentProject.Connection can be called in the VBE Immediate window to see what an actual connection string looks like. Both the Access help file and the section “Rolling Your Own Connection String” later in this chapter also provide some guidance. Specifying a Cursor Location In contrast with DAO, when you create an ADO connection, you specify a cursor, which is a database ele- ment that controls record navigation, data updateability, and the tracking of changes made by other users. There are two types of cursor: client side and server side. Choose the cursor you want to use by setting the Command or Recordset object’s CursorLocation property to one of the following two constants before you open the connection or recordset: rs.CursorLocation = adUseClient ‘ Use a client-side cursor rs.CursorLocation = adUseServer ‘ Default. Use a server-side cursor As with most things in life, the choice comes with a tradeoff. In most cases, server-side cursors are a bit faster; however, client-side cursors offer a little more functionality. When you set CursorLocation at the connection level, you are specifying the location that will be used by default when you create recordsets against that connection. You can override this setting at the recordset level at any point during code execution. Server-Side Cursors When using a server-side cursor (the default in ADO), the records contained in the recordset are cached on the server. The major benefit is significantly reduced network traffic thus improving application per- formance. The downside is that server resources are consumed for every active client — the more clients (and the more data being cached), the more server resources are consumed. It’s important to plan ahead to ensure your server has sufficient resources to do the job. Server-side cursors enable you to use both keyset and dynamic cursors, and also support direct positional updates, which are fast and avoid update collisions. You can also use each connection for more than one operation. For example, you can have a recordset open and still execute multiple update queries without having to open an additional connection. Server- side cursors are best for inserting, updating, and deleting records, and they enable you to have multiple active statements on the same connection. Client-Side Cursors If the data source to which you’re connecting doesn’t support server-side cursors, you have no choice but to use a client-side cursor. With non-keyset client-side cursors, the server sends the entire recordset to the client across the network. Because the client must now provide and manage the resources neces- sary to cache the records, this places a significant load on both the network and the client. Needless to say, this reduces your application’s overall performance. One benefit of using client-side cursors is that, once the data is cached, subsequent access to the data is much faster than with server side cursors because the data resides locally. A second benefit is that the 195 Chapter 7: Using ADO to Access Data 47033c07.qxd:WroxProgRef 3/30/07 12:25 AM Page 195 application is generally more scalable because the resources required to run the application are distrib- uted among many clients, rather than loading down a single server. Rolling Your Own Connection String The following is the output of the CurrentProject.Connection property for the Northwind 2007 sample database. (To shorten the file paths for this example, the database is moved to a different folder.) Provider=Microsoft.ACE.OLEDB.12.0; User ID=Admin; Data Source=C:\Databases\Northwind2007.accdb; Mode=Share Deny None; Extended Properties=”“; Jet OLEDB:System database=C:\Databases\System.mdw; Jet OLEDB:Registry Path= Software\Microsoft\Office\12.0\Access\Access Connectivity Engine; Jet OLEDB:Database Password=”“; Jet OLEDB:Engine Type=6; Jet OLEDB:Database Locking Mode=0; Jet OLEDB:Global Partial Bulk Ops=2; Jet OLEDB:Global Bulk Transactions=1; Jet OLEDB:New Database Password=”“; Jet OLEDB:Create System Database=False; Jet OLEDB:Encrypt Database=False; Jet OLEDB:Don’t Copy Locale on Compact=False; Jet OLEDB:Compact Without Replica Repair=False; Jet OLEDB:SFP=False; Jet OLEDB:Support Complex Data=True Note there is one connection option listed that was not available in previous versions of Access. That’s the Jet OLEDB:Support Complex Data=True option, which explicitly requires that the connection provider support the complex data in Access 2007 for Multi-valued and Attachment field types. When creating a connection string, only the following five parameters need to be supplied: ❑ Provider (including version) ❑ User ID ❑ Password or database password (if applicable) ❑ Data source ❑ System database (if applicable) In the case of an ADP, the requirements for the connection string are slightly different. You need to sup- ply the provider, security info, data source, integrated security, initial catalog, and the data provider. All the other information is contained in the ADP Data Link for the ADP. Here’s an example of an ADP con- nection string: Provider=Microsoft.Access.OLEDB.10.0; Persist Security Info=False; 196 Chapter 7: Using ADO to Access Data 47033c07.qxd:WroxProgRef 3/30/07 12:25 AM Page 196 Data Source=<SQL machine name>; Integrated Security=SSPI; Initial Catalog=SampleDB; Data Provider=SQLOLEDB.1 Creating and Using a Data Link Instead of using CurrentProject.Connection to set your connection string, or writing your own, you can use a Data Link. You can specify the Data Link filename as a parameter of the Connection object, but you have to create a UDL file first. To create a custom data link, do the following: 1. Open Windows Explorer and select File ➪ New ➪ Text Document. 2. Rename the file to something meaningful, and change its file extension to .UDL. 3. Click Yes in the message box that Windows subsequently displays to rename the extension. 4. Double-click the new UDL file and the Data Link Properties dialog box opens (see Figure 7-1). Figure 7-1 5. On the Provider tab, select Microsoft Office 12.0 Access Database Engine OLE DB Provider, and click the Next button. 6. In the Data Source text box, type the name and path to your Access database. For example: C:\Users\<user>\Northwind 2007.accdb 7. Click the Test Connection button. If all went well, the message box shown in Figure 7-2 displays. 197 Chapter 7: Using ADO to Access Data 47033c07.qxd:WroxProgRef 3/30/07 12:25 AM Page 197 [...]... manner in which the recordset is populated with data The columns can contain data from a provider such as Access or the SQL Server, references to another recordset, values derived from a calculation on a row, and so on 206 47 033 c07.qxd:WroxProgRef 3/ 30/07 12:25 AM Page 207 Chapter 7: Using ADO to Access Data Here’s a simple example from the NorthwindCS sample database (in the Chapter 19 code download),... dataset 218 47 033 c07.qxd:WroxProgRef 3/ 30/07 12:25 AM Page 219 Chapter 7: Using ADO to Access Data Creating Schema Recordsets You’re no doubt familiar with using recordsets to access and manipulate data But ADO also allows you to open recordsets that contain information about your database’s tables Of course, you can get at this information using ADOX, but some details are more readily accessed using... rs As Recordset ‘ Set the required properties With cmd CommandText = strSQL CommandType = adCmdUnknown ActiveConnection = CurrentProject.Connection End With 202 47 033 c07.qxd:WroxProgRef 3/ 30/07 12:25 AM Page 2 03 Chapter 7: Using ADO to Access Data ‘ Exectute the command Set rs = cmd.Execute ‘ Return the Recordset and Clean up Set ExecuteFromCommand = rs Set cmd = Nothing End Function Specifying Command... relationship in which the parent is the container recordset and the child is the contained recordset The Shape statement 207 47 033 c07.qxd:WroxProgRef 3/ 30/07 12:25 AM Page 208 Chapter 7: Using ADO to Access Data enables you to create a shaped recordset, which you can then access programmatically or through a visual control You issue the Shape statement as you do any other ADO command text This simple... committed In addition, the ADO Update method is optional You don’t need to use it; however, you’ll earn yourself a runtime error if you attempt to close a recordset 212 47 033 c07.qxd:WroxProgRef 3/ 30/07 12:25 AM Page 2 13 Chapter 7: Using ADO to Access Data without committing or canceling any changes, so it is recommended that you explicitly use it anyway For example: With rs Open “Shippers”, cn, adOpenDynamic,... cursor must be used and you must be sure to open the recordset using the adLockBatchOptimisticLockType option Here’s an example: With rs CursorLocation = adUseClient 2 13 47 033 c07.qxd:WroxProgRef 3/ 30/07 12:25 AM Page 214 Chapter 7: Using ADO to Access Data CursorType = adOpenKeyset LockType = adLockBatchOptimistic Open “Customers”, cn ‘Find the right record to edit Find “Country = ‘USA’“ Do While Not EOF... the recordset’s connection rs.ActiveConnection = CurrentProject.Connection ‘ Make a change and save it again rs!Fax = “555-1 234 ” rs.Update Kill strADTGFile rs.Save strADTGFile, adPersistADTG ‘ Clean up 215 47 033 c07.qxd:WroxProgRef 3/ 30/07 12:25 AM Page 216 Chapter 7: Using ADO to Access Data rs.Close Set rs = Nothing The final example in this section opens the file again to demonstrate that the goal is... MsgBox “There was no record for the Item Specified” GetPrice = 0 Else GetPrice = rs(“Price”).Value End If ‘ Clean up Set rs = Nothing Set cmd = Nothing End Function 2 03 47 033 c07.qxd:WroxProgRef 3/ 30/07 12:25 AM Page 204 Chapter 7: Using ADO to Access Data Creating Your Own Parameters It’s quite simple to create your own parameters on-the-fly in code using ADO To create the parameter for a given query, call... Disconnect The Recordset object exposes other events as well Refer to Appendix F for a list of all the available events for the Connection and Recordset objects 222 47 033 c07.qxd:WroxProgRef 3/ 30/07 12:25 AM Page 2 23 Chapter 7: Using ADO to Access Data Testing the State Proper ty If you choose not to rely on events, you can always test the object’s State property It returns a value that indicates the status... RollbackTrans, you are operating on the most recently opened transaction To resolve higher-level transactions, you must close or roll back the current transaction 199 47 033 c07.qxd:WroxProgRef 3/ 30/07 12:25 AM Page 200 Chapter 7: Using ADO to Access Data When you call CommitTrans or RollbackTrans, the appropriate action is taken, and the transaction is closed If you set the Connection object’s Attributes . to Access Data 47 033 c06.qxd:WroxProgRef 3/ 30/07 12:25 AM Page 189 47 033 c06.qxd:WroxProgRef 3/ 30/07 12:25 AM Page 190 Using ADO to Access Data Data Access Objects (DAO) was the default data access. following: Provider=Microsoft.ACE.OLEDB.12.0; 47 033 c07.qxd:WroxProgRef 3/ 30/07 12:25 AM Page 191 Similarly, Access uses the Access OLE DB provider when you create an Access Data Project (ADP) against the. default reference, Access 2002 was already in beta, and the ADO reference remained. The issue was finally resolved in Access 20 03 by refer- encing both object libraries by default. In Access 2007,