This technical note describes the DAO record field exchange (DFX) mechanism. To help understand what is happening in the DFX routines, the DFX_Text function will be explained in detail as an example. As an additional source of information to this technical note, you can simply examine the code for the other the individual DFX functions.You probably won’t need a custom DFX routine as often as you might need a custom RFX routine (used with ODBC database classes).
This technical note contains:
DFX Overview
The DAO record field exchange mechanism (DFX) is used to simplify the procedure of retrieving and updating data when using the CDaoRecordset class. The process is simplified through the use of data members of the CDaoRecordset class. By deriving from CDaoRecordset, you can add data members to the derived class representing each field in a table or query. This “static binding” mechanism is simple, but it may not be the data fetch/update method of choice for all applications. DFX retrieves every bound field each time the current record is changed. If you are developing a performance-sensitive application that does not require fetching every field when currency is changed, “dynamic binding” via CDaoRecordset::GetFieldValue and CDaoRecordset::SetFieldValue may be the data access method of choice.
Note DFX and dynamic binding are not mutually exclusive, so a hybrid use of static and dynamic binding can be used.
Example 1 - Use of DAO Record Field Exchange only
(assumes CDaoRecordset - derived class CMySet
already open)
// Add a new record to the customers table
myset.AddNew();
myset.m_strCustID = _T("MSFT");
myset.m_strCustName = _T("Microsoft");
myset.Update();
Example 2 - Use of dynamic binding only
(assumes using CDaoRecordset class, rs
, and it is already open)
// Add a new record to the customers table
COleVariant varFieldValue1 ( _T("MSFT"), VT_BSTRT );
//Note: VT_BSTRT flags string type as ANSI, instead of UNICODE default
COleVariant varFieldValue2 (_T("Microsoft"), VT_BSTRT );
rs.AddNew();
rs.SetFieldValue(_T("Customer_ID"), varFieldValue1);
rs.SetFieldValue(_T("Customer_Name"), varFieldValue2);
rs.Update();
Example 3 - Use of DAO Record Field Exchange and dynamic binding
(assumes browsing employee data with CDaoRecordset-derived class emp
)
// Get the employee's data so that it can be displayed
emp.MoveNext();
// If user wants to see employee's photograph,
// fetch it
COleVariant varPhoto;
if (bSeePicture)
emp.GetFieldValue(_T("photo"), varPhoto);
// Display the data
PopUpEmployeeData(emp.m_strFirstName,
emp.m_strLastName, varPhoto);
The DFX mechanism works in a similar fashion to the record field exchange (RFX) mechanism utilized by the MFC ODBC classes. The priciples of DFX and RFX are the same but there are numerous internal differences. The design of the DFX functions was such that virtually all the code is shared by the individual DFX routines. At the highest level DFX only does a few things.
At the heart of the DFX mechanism is the CDaoRecordset derived class’s DoFieldExchange function. This function dispatches calls to the individual DFX functions of an appropriate operation type. Prior to calling DoFieldExchange the internal MFC functions set the operation type. The following list shows the various operation types and a brief description.
Operation | Description |
AddToParameterList | Builds PARAMETERS clause |
AddToSelectList | Builds SELECT clause |
BindField | Sets up binding structure |
BindParam | Sets parameter values |
Fixup | Sets NULL status |
AllocCache | Allocates cache for dirty check |
StoreField | Saves current record to cache |
LoadField | Restores cache to member values |
FreeCache | Frees cache |
SetFieldNull | Sets field status & value to NULL |
MarkForAddNew | Marks fields dirty if not PSEUDO NULL |
MarkForEdit | Marks fields dirty if don't match cache |
SetDirtyField | Sets field values marked as dirty |
In the next section, each operation will be explained in more detail for DFX_Text.
The most important feature to understand about the DAO record field exchange process is that it uses the GetRows function of the ICDaoRecordset object. The DAO GetRows function can work in several ways. This technical note will only briefly describe GetRows as it is outside of the scope of this technical note.
DAO GetRows can work in several ways.
What Your Custom DFX Routine Does
It is apparent from this discussion that the most important operation implemented in any DFX function must be the ability to set up the required data structures to successfully call GetRows. There are a number of other operations that a DFX function must support as well, but none as important nor complex as correctly preparing for the GetRows call.
The use of DFX is described in the online documentation. Essentially, there are 2 requirements. First, members must be added to the CDaoRecordset derived class for each bound field and parameter. Following this CDaoRecordset::DoFieldExchange should be overridden. Note that the data type of the member is important. It should match the data from the field in the database or at least be convertable to that type. For example a numeric field in database, such as a long integer, can always be converted to text and bound to a CString member, but a text field in a database may not necessarily be converted to a numeric representation, such as long integer and bound to a long integer member. DAO and the Microsoft Jet database engine are responsible for the conversion (rather than MFC).
As mentioned previously, probably the best way to explain how DFX works is to simply work through an example. For this purpose going through the internals of DFX_Text should work quite well to help provide at least a basic understanding of DFX.
AddToParameterList
This operation builds the SQL PARAMETERS clause (“Parameters <param name>, <param type> ... ;
”) required by Jet. Each parameter is named and typed (as specified in the RFX call). See the function CDaoFieldExchange::AppendParamType function to see the names of the individual types. In the case of DFX_Text, the type used is text
.
AddToSelectList
Builds the SQL SELECT clause. This is pretty straight forward as the column name specified by the DFX call is simply appended (“SELECT <column name>, ...
”).
BindField
- By far the most complex of the operations. As mentioned previously this is where the DAO binding structure used by GetRows is set up. As you can see from the code in DFX_Text the types of information in the structure include the DAO type used (DAO_CHAR or DAO_WCHAR in the case of DFX_Text). Additionally, the type of binding used is also set up. In an earlier section GetRows was described only briefly, but it was sufficient to explain that the type of binding used by MFC is always direct address binding (DAOBINDING_DIRECT). In addition for variable length column binding (like DFX_Text) callback binding is used so that MFC can control the memory allocation and specify an address of the correct length. What this means is that MFC can always tell DAO “where” to put the data, thus allowing binding directly to member variables. The rest of the binding structure is filled in with things like the address of the memory allocation callback function and the type of column binding (binding by column name).
BindParam
This is a simple operation which calls SetParamValue with the parameter value specified in your parameter member.
Fixup
Fills in the NULL status for each field.
SetFieldNull
This operation only marks each field status as NULL and sets the member variable’s value to PSEUDO_NULL.
SetDirtyField
Calls SetFieldValue for each field marked dirty.
All of the remaining operations only deal with using the data cache. The data cache is an extra buffer of the data in the current record that is used to make certain things simpler. For instance “dirty” fields can be automatically detected. As described in the online documentation it can be turned off completely or at the field level. The implementation of the buffer utilizes a map. This map is used to match up dynamically allocated copies of the data with the address of the “bound” field (or CDaoRecordset derieved data member).
AllocCache
Dynamically allocates the cached field value and adds it to the map.
FreeCache
Deletes the cached field value and removes it from the map.
StoreField
Copies the current field value into the data cache.
LoadField
Copies the cached value into the field member.
MarkForAddNew
Checks if current field value is non-NULL and marks it dirty if necessary.
MarkForEdit
Compares current field value with data cache and marks dirty if necessary.
Tip Model your custom DFX routines on the existing DFX routines for standard data types.