The CursorAdapter class is new to FoxPro 8.0. This class “knows” how to access remote data and is loaded with “essential ingredients” necessary to work with a variety of data formats.
The examples in this book concentrate on ODBC connections to remote data. However, you could apply the same concepts to the other data formats supported by the CursorAdapter.
The essential properties for working with remote data are KeyFieldList, SelectCmd, Tables, UpdatableFieldList, and UpdateNameList. Notice the similarity between these CursorAdapter property names and the parameters passed to the CursorSetProp() function.
The values of the properties are identical as well. Rather than using the previous code to generate the values, FoxPro provides a DataEnvironment builder that enables you to visually build the required property values.
One property that does not map directly between the CursorAdapter and the CursorSetProp() function is the SelectCmd property of the CursorAdapter class. The CursorSetProp() equivalent is the “SQL” parameter. However, the SQL value of the underlying cursor is read-only, whereas the SelectCmd is read/write.
The SelectCmd property determines what information is retrieved each time a call is made to the back-end database. The SelectCmd property contains an SQL statement. For example, the following value for SelectCmd would return all the records from the SysCodes table:
SELECT * From SysCodes
You call the CursorFill() method of the CursorAdapter class to actually submit the request to the back end.
The CursorAdapter class does not connect to the back-end database directly. The numeric connection handle returned by the SQLConnect() or SQLStringConnect() functions is stored in the DataSource property of the cursor. Setting the UseDataSource property of the
CursorAdapter to True indicates that the CursorAdapter obtains the connection handle from
Chapter 9: Cursor and CursorAdapter 133
the data environment. In the next chapter I’ll explain how the new DataEnvironment class manages the connection process.
MyCursorAdapter
Like the native Cursor class, the CursorAdapter class does not provide an object-oriented interface to common data methods. The design objective for MyCursorAdapter is to develop an interface that is similar to MyCursor. In fact, most FoxPro commands and functions work regardless of whether the cursor contains remote or local data. Therefore, most of the commands and methods for MyCursorAdapter are identical to those for MyCursor.
For example, MyCursor.New() inserts a new record into a cursor and assigns a primary key value. MyCursorAdapter.New() also inserts a record into a cursor and assigns a primary key. MyCursor.Go() accepts a key value and makes that record the current record.
MyCursorAdapter.Go() also accepts a key value and makes that the current record.
Maintaining a uniform interface between the two classes makes it easier for the developer to learn how to use the classes. Additionally, the DataEnvironment class developed in the next chapter uses the same code whether you are working with local (MyCursor) or remote (MyCursorAdapter) data.
The CursorAdapter class has a LOT of functionality that I’m not going to explore here. The purpose of this section is to illustrate one approach to using remote data in a manner similar to the approach illustrated with MyCursor.
Methods
Most of the method code in MyCursorAdapter closely resembles the code presented for MyCursor. To avoid redundancy, the code is not repeated here.
There are a few differences worth noting, however; these are explained in the Requery(), Go(), and Open() sections that follow.
Requery()
Retrieving data from a remote database is controlled by the SelectCmd property and the CursorFill() method. The custom Requery() method gathers information from the class to dynamically build an SQL statement prior to calling the CursorFill() method. This allows me to simulate many of the methods for MyCursor, such as SetFilter(), SetOrder(), and Go().
One other aspect of the CursorAdapter class is that you need to capture the data type of the primary key field. This ensures that the primary key generation works properly. The IF statement ensures that the value is set only once, and is not evaluated as part of each Refresh().
*--MyCursorAdapter.Requery()
134 Build Your Own Framework with Visual FoxPro
lcPKField = ALLTRIM(.ALIAS)+"."+ALLTRIM(.KEYFIELDLIST) .cPktype = VARTYPE(&lcPKField)
ENDIF ENDWITH
Go()
The Go() method accepts two parameters: the value to search for and optionally the tag to search against. In MyCursor.Go(), a SEEK() or LOCATE moves the record pointer.
MyCursorAdapter.Go translates the parameters into a filter expression and calls Requery() to update the cursor.
The code for MyCursorAdapter.Go() is shown here:
*--MyCursorAdapter.Go() LPARAMETERS tuID,tcTag
*--Build a filter string
*--If a tag is not specified, use the primary key
*) identified in .KEYFIELDLIST LOCAL lcTag
lcTag = IIF(EMPTY(tcTag),THIS.KEYFIELDLIST, tcTag)
*--Convert to correct datatype (Needed for strings passed for numeric info
DO CASE
Unlike the Cursor class, you cannot open, or USE, a cursor adapter outside of the data environment. Accordingly, MyCursorAdapter does not have an Open() method.
Chapter 9: Cursor and CursorAdapter 135
Summary
In this chapter you have learned to create a consistent, object-oriented approach to working with local and remote data. The approach outlined in this chapter considered buffering strategies; populating primary key values; and adding, updating, and deleting records. It also illustrated some of the key differences between remote and local data.
Updates and corrections to this chapter can be found on Hentzenwerke’s Web site, www.hentzenwerke.com. Click “Catalog” and navigate to the page for this book.
136 Build Your Own Framework with Visual FoxPro
Chapter 10: Business Objects 137