Yesterday, you explored the ODBC and DAO APIs and were introduced to the MFC wrapper classes for both. Today, you will create a simple application using the wrapper classes and discuss the data binding that takes place. You will also look at what the API is doing through the use of the wrapper classes.
Today you will
This chapter focuses on using the wrapper classes that MFC provides for the ODBC and DAO APIs. You do this by creating a simple application to list addresses for your friends, like the one in Figure 15.1. The database that you will be using was created with Access 97 and will contain a single table (Addresses). This table will contain the fields shown in Figure 15.1:
Figure 15.1 : The Address book table (Addresses) structure.
To simplify things, you examine the application development code, using the MFC AppWizard for creating your data bindings. After you do this, you will take a closer look at how the table data can be manually bound to the application's variables.
Day 14, "Legacy Database APIs," provides an introduction to the ODBC API and to the MFC wrappers for the API. One important item that Day 14 doesn't discuss is the CRecordView class. This class doesn't really wrap the API so much as it creates a go-between data-binding class that fits into the MFC's Document/View architecture. CRecordView is derived from the CFormView class, which ultimately is derived from CView. The view classes provide the user interface mechanisms to display the data and handle Windows messages. The CRecordSet and CDatabase classes are closely coupled with CRecordView, as you will see in your application code. It contains a member variable to handle the actual data cursor. It does this in the form of a record pointer. If you have had the opportunity to program database applications the hard way, you will definitely appreciate this class. The CRecordView also contains a pointer to the CRecordSet class.
NOTE |
If you are unfamiliar with MFC and the MFC Document/View architecture, the following sections might appear to be a broad step from the explanation of the API to the actual implementation of the application. This chapter does this to highlight certain aspects of implementing database applications; however, it does so using the most widely used approach. |
Let's jump right in and create the sample application.
Before the AppWizard creates the application, the database and ODBC data source must exist. You can use the Control Panel ODBC applet to determine whether a database and ODBC data source exist for the targeted application.
NOTE |
The application code is included on the accompanying CD-ROM, which includes the AddressBook database. |
From the File menu, select New to start the MFC AppWizard. The screen shown in Figure 15.2 appears. Based on your system file structure and where your database and application files reside, fill in the appropriate information shown in Figure 15.2. In this case, you are creating an executable that will be MFC-based.
Figure 15.2 : MFC AppWizard Step 1: Creating the application.
Figure 15.3 shows the screen where you specify the database support for your application. At this point, it is asking whether you want database support. (You can choose to skip this selection, by choosing None, and then put it in later.) Select Database View Without File Support. After you select the database support, the next step is to set the data source. For your example, select ODBC as shown in Figure 15.3.
Figure 15.3 : MFC AppWizard Step 2: Defining database requirements.
You then select the table(s) that you would like to have support for.
The AppWizard will build the data relationships for the recordset that relates to the tables you select. For every table that you select, the wizard will create a CRecordSet-based class that wraps the table. You'll come back to this when you look at the code. Figure 15.4 shows the classes that AppWizard will create.
Figure 15.4 : MFC AppWizard Step 6: Files created.
You only need to concern yourself with the CAddressBookODBCView
class and the CAddressBookODBCSet class. These two classes
contain the information that will help you understand how the
data is passed from the data source to the application. Listing
15.1 is the definition file for the CAddressBookODBCSet
class.
Listing 15.1 The CADDRESSBOOKODBCSET Class Declaration
1: // AddressBookODBCSet.h : interface of the 2: // CAddressBookODBCSet class 3: /////////////////////////////////////////////////////// 4: 5: #if !defined(AFX_ADDRESSBOOKODBCSET_H__D21600E1_4140_ 11D2_9D78_000000000000__INCLUDED_) 6: #define AFX_ADDRESSBOOKODBCSET_H__D21600E1_ 4140_11D2_9D78_000000000000__INCLUDED_ 7: 8: #if _MSC_VER >= 1000 9: #pragma once 10: #endif // _MSC_VER >= 1000 11: 12: class CAddressBookODBCSet : public CRecordset 13: { 14: public: 15: CAddressBookODBCSet(CDatabase* pDatabase = NULL); 16: DECLARE_DYNAMIC(CAddressBookODBCSet) 17: 18: // Field/Param Data 19: //{{AFX_FIELD(CAddressBookODBCSet, CRecordset) 20; long m_ID; 21: CString m_Last_Name; 22: CString m_First_Name; 23: CString m_Street; 24: CString m_City; 25: CString m_State; 26: long m_Zip; 27: CString m_Phone; 28: //}}AFX_FIELD 29: 30: // Overrides 31: // ClassWizard generated virtual function overrides 32: //{{AFX_VIRTUAL(CAddressBookODBCSet) 33: public: 34: virtual CString GetDefaultConnect(); // Default connection string 35: virtual CString GetDefaultSQL(); // default SQL for Recordset 36: virtual void DoFieldExchange(CFieldExchange* pFX); // RFX support 37: //}}AFX_VIRTUAL 38: 39: // Implementation 40: #ifdef _DEBUG 41: virtual void AssertValid() const; 42: virtual void Dump(CDumpContext& dc) const; 43: #endif 44: 45: }; 46: 47: //{{AFX_INSERT_LOCATION}} 48: // Microsoft Developer Studio will insert additional 49: // declarations immediately before the preceding line. 50: 51: #endif // !defined(AFX_ADDRESSBOOKODBCSET_H__D21600E1_ 4140_11D2_9D78_000000000000__INCLUDED_)
Notice that line 19 is where the data mapping starts. The AFX_FIELD declaration indicates that this is field data for the recordset. Just think of the number of SQLBindColumn calls that you would have to make to declare each field if the table were very large.
The MFC DDX/DDV message-handling mechanism defines the connection between the table column data and the applications variable used to present that data. The other part of the binding that takes place here is the actual data attribute definition. The CString class is a wrapper for a C character string. The CString class is mapped to the character data represented in the columns. The long is mapped to the data type for the Zip code in the Access database. In this case, it is defined as an integer.
Let's take a look at the CRecordView-based class. Here
you see the implementation file for the CAddressBookODBCView
class in Listing 15.2.
Listing 15.2 The CADDRESSBOOKODBCVIEW Class Declaration
1: // AddressBookODBCView.h : interface of the 2: // CAddressBookODBCView class 3: ///////////////////////////////////////////////////// 4: 5: #if !defined(AFX_ADDRESSBOOKODBCVIEW_H__D21600DF_4140_ 11D2_9D78_000000000000__INCLUDED_) 6: #define AFX_ADDRESSBOOKODBCVIEW_H__D21600DF_4140_11D2_ 9D78_000000000000__INCLUDED_ 7: 8: #if _MSC_VER >= 1000 9: #pragma once 10: #endif // _MSC_VER >= 1000 11: 12: class CAddressBookODBCSet; 13: 14: class CAddressBookODBCView : public CRecordView 15: { 16: protected: // create from serialization only 17: CAddressBookODBCView(); 18: DECLARE_DYNCREATE(CAddressBookODBCView) 19: 20: public: 21: //{{AFX_DATA(CAddressBookODBCView) 22: enum{ IDD = IDD_ADDRESSBOOKODBC_FORM }; 23: CAddressBookODBCSet* m_pSet; 24: // NOTE: the ClassWizard will add data members here 25: //}}AFX_DATA 26: 27: // Attributes 28: public: 29: CAddressBookODBCDoc* GetDocument(); 30: 31: // Operations 32: public: 33: 34: // Overrides 35: // ClassWizard generated virtual function overrides 36: //{{AFX_VIRTUAL(CAddressBookODBCView) 37: public: 38: virtual CRecordset* OnGetRecordset(); 39: virtual BOOL PreCreateWindow(CREATESTRUCT& cs); 40: protected: 41: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV // support 42: virtual void OnInitialUpdate(); // called first time after // construct 43: virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); 44: virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo); 45: virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo); 46: //}}AFX_VIRTUAL 47: 48: // Implementation 49: public: 50: virtual ~CAddressBookODBCView(); 51: #ifdef _DEBUG 52: virtual void AssertValid() const; 53: virtual void Dump(CDumpContext& dc) const; 54: #endif 55: 56: protected: 57: 58: // Generated message map functions 59: protected: 60: //{{AFX_MSG(CAddressBookODBCView) 61: // NOTE - The ClassWizard will add and remove member // functions here. 62: // DO NOT EDIT what you see in these blocks of generated // code! 63: //}}AFX_MSG 64: DECLARE_MESSAGE_MAP() 65: }; 66: 67: #ifndef _DEBUG // debug version in AddressBookODBCView.cpp 68: inline CAddressBookODBCDoc* CAddressBookODBCView::GetDocument() 69: { return (CAddressBookODBCDoc*)m_pDocument; } 70: #endif 71: 72: /////////////////////////////////////////////////////////////////// 73: 74: //{{AFX_INSERT_LOCATION}} 75: // Microsoft Developer Studio will insert additional declarations 76: // immediately before the preceding line. 77: 78: #endif // !defined(AFX_ADDRESSBOOKODBCVIEW_H__D21600DF_4140_ 11D2_9D78_000000000000__INCLUDED_)
The CAddressBookODBCView class contains quite a few more function members, but most are related to the Document/View architecture of MFC. Notice on line 23 that you have a pointer to the CAddressBookODBCSet class in the CAddressBookODBCView class. This pointer is the primary link between the API wrapping and the Document/View architecture. The CAddressBookODBCView class contains a document that is the application's data keeper. Data is passed between the view and dialog classes and the GUI via the MFC DDX/DDV data exchange mechanisms. The view gets access to the document directly via GetDocument; the document pumps notifications (and hints) to the view via the CDocument::UpdateAllViews/CView;;OnUpdate coupling. For data to be bound from a database to the application, the application's view must know about the data structure. The m_pSet pointer is the mechanism that accomplishes this. Let's take a look at what the Document class contains. Notice that the document contains the RecordSet (line 21).
Something very interesting happens in Listing 15.3. By looking
at the wrapper declarations for your application, you have learned
how the data binding takes place. The document class represents
the application's memory. The view class is your window into that
data. But you are working with databases, and the data in this
case is stored on disk. The RecordSet is the API wrapper
that will provide the link to the data in the database. By containing
the recordset, the document class essentially maps the data into
the application's memory.
Listing 15.3 The CADDRESSBOOKODBCDOC Class Declaration
1: // AddressBookODBCDoc.h : interface of the CAddressBookODBCDoc // class 2: // 3: /////////////////////////////////////////////////////////////////// 4: 5: #if !defined(AFX_ADDRESSBOOKODBCDOC_H__D21600DD_4140_11D2_9D78_ 000000000000__INCLUDED_) 6: #define AFX_ADDRESSBOOKODBCDOC_H__D21600DD_4140_11D2_9D78_ 000000000000__INCLUDED_ 7: 8: #if _MSC_VER >= 1000 9: #pragma once 10: #endif // _MSC_VER >= 1000 11: 12: 13: class CAddressBookODBCDoc : public CDocument 14: { 15: protected: // create from serialization only 16: CAddressBookODBCDoc(); 17: DECLARE_DYNCREATE(CAddressBookODBCDoc) 18: 19: // Attributes 20: public: 21: CAddressBookODBCSet m_addressBookODBCSet; 22: 23: // Operations 24: public: 25: 26: // Overrides 27: // ClassWizard generated virtual function overrides 28: //{{AFX_VIRTUAL(CAddressBookODBCDoc) 29: public: 30: virtual BOOL OnNewDocument(); 31: //}}AFX_VIRTUAL 32: 33: // Implementation 34: public: 35: virtual ~CAddressBookODBCDoc(); 36: #ifdef _DEBUG 37: virtual void AssertValid() const; 38: virtual void Dump(CDumpContext& dc) const; 39: #endif 40: 41: protected: 42: 43: // Generated message map functions 44: protected: 45: //{{AFX_MSG(CAddressBookODBCDoc) 46: // NOTE - The ClassWizard will add and remove member 47: // functions here. DO NOT EDIT what you see in these 48 // blocks of generated code ! 49: //}}AFX_MSG 50: DECLARE_MESSAGE_MAP() 51: }; 52: 53: /////////////////////////////////////////////////////////////////// 54: 55: //{{AFX_INSERT_LOCATION}} 56: // Microsoft Developer Studio will insert additional declarations 57: // immediately before the previous 57: line. 58: 59: #endif // !defined(AFX_ADDRESSBOOKODBCDOC_H__D21600DD_4140_11D2_ 9D78_000000000000__INCLUDED_)
You have now created an application that contains the ODBC wrappers that you will need to interface to your database. However, the AppWizard really can't determine what you plan to do with the data. If the application were to be built and executed at this point, you would see something similar to Figure 15.5.
Figure 15.5 : The Address Book ODBC application. First run.
For the application to do any real work, you first have to create controls for the data to be displayed in.
Look at Listing 15.4 to see how much the AppWizard really does
for you.
Listing 15.4 The ONINITIALUPDATE Implementation
1: void CAddressBookODBCView::OnInitialUpdate() 2: { 3: m_pSet = &GetDocument()->m_addressBookODBCSet; 4: CRecordView::OnInitialUpdate(); 5: }
The OnInitialUpdate function member of your view class will set your recordset pointer. Notice that it gets the RecordSet information from the document-remember our discussion about the Document/View and how the RecordView tied everything together.
In Listing 15.5, the two functions GetDefaultConnect()
and GetDefaultSQL() are used. The GetDefaultConnect
function member will provide the data source information that
you need to pass to the database open command. The GetDefaultSQL
function member will pass back the defining information for your
fetch routines. (In this case, the SQL statement that queries
the Addresses table).
Listing 15.5 The GETDEFAULTCONNECT and GETDEFAULTSQL
Implementations
1: CString CAddressBookODBCSet::GetDefaultConnect() 2: { 3: return _T("ODBC;DSN=AddressBook"); 4: } 5: 6: CString CAddressBookODBCSet::GetDefaultSQL() 7: { 8: return _T("[Addresses]"); 9: } 10: 11: void CAddressBookODBCSet::DoFieldExchange(CFieldExchange* pFX) 12: { 13: //{{AFX_FIELD_MAP(CAddressBookODBCSet) 14: pFX->SetFieldType(CFieldExchange::outputColumn); 15: RFX_Long(pFX, _T("[ID]"), m_ID); 16: RFX_Text(pFX, _T("[Last Name]"), m_Last_Name); 17: RFX_Text(pFX, _T("[First Name]"), m_First_Name); 18: RFX_Text(pFX, _T("[Street]"), m_Street); 19: RFX_Text(pFX, _T("[City]"), m_City); 20: RFX_Text(pFX, _T("[State]"), m_State); 21: RFX_Long(pFX, _T("[Zip]"), m_Zip); 22: RFX_Text(pFX, _T("[Phone]"), m_Phone); 23: //}}AFX_FIELD_MAP 24: }
In Listing 15.6, notice line 4 from the modified OnInitialUpdate
function. To open the recordset, simply call the Open
function member of the recordset and pass the SQL string. This
isn't always necessary. The GetDefaultSQL function member
contains a shorthand version of the statement listed previously.
If the SELECT statement needs to change, simply insert
the SQL statement in the recordset's OPEN function.
Listing 15.6 The Modified ONINITIALUPDATE Implementation
1: void CAddressBookODBCView::OnInitialUpdate() 2: { 3: m_pSet = &GetDocument()->m_addressBookODBCSet; 4: m_pSet->Open(CRecordSet::snapshot,"SELECT * FROM Addresses"); 5: CRecordView::OnInitialUpdate(); 6: }
NOTE |
It is important to note here that the OnInitialUpdate routine will actually perform the connecting and opening of the data source. It then will set the pointer to the recordset. |
By using the AppWizard to generate this application, you didn't use the CDatabase class to connect or open the data source. By letting AppWizard define the recordset parameters, the code generated gets around this. If the support selected during the AppWizard phase does not include this support, the application developer would have to add a CDatabase class and CRecordSet class implementation in the CRecordView. More work, but essentially the same result.
What you haven't learned is how the table data is transferred to the application's data. Inside MFC database support is the Record Field Exchange (RFX) mechanism. Similar to Dynamic Data Exchange (DDX), the RFX layer performs the underlying field-to-data transfers. This is done at the message interface level.
You have now created an application that uses the ODBC API. You will next explore the DAO API.
The steps needed to create the DAO application are similar to the steps needed to create an MFC ODBC application. However, there are some minor differences. When asked for database support (refer to Figure 15.3), select Database Support with File Support (the last selection). Remember the discussion in Day 14 concerning the two APIs. ODBC uses a data source, which can be network-resident and doesn't rely on the file system. DAO uses the Jet engine, which is file-based. After this selection is made, the steps are identical to those for the ODBC application.
Many applications that you might run across will obviously have more than one recordset defined to accept a query on just one table. The AppWizard gives the application developer a starting point. As the application is created, notice that the files produced by the AppWizard are almost identical to those created when you built the ODBC application. The MFC wrapper classes for the DAO API were designed to closely match those created for the ODBC API, even though the APIs are completely different. Taking a good look under the hood is beyond the scope of this book, but if you have the opportunity, look at the MFC wrapper code for the DAO API classes that AppWizard creates. If you compare the implementation of the CDaoRecordView class and the CRecordView class, you will also see the similarities. Remember that the view is the coupling class to the MFC Document/View architecture. Most of the differences will appear at the recordset class wrappers.
Let's take a look at in Listing 15.7 the class implementations
that the AppWizard generated for us. First, look at the Record
Set class.
Listing 15.7 The DAO CADDRESSBOOKDAOSET Class Declaration
1: // AddressBookDAOSet.h : interface of the CAddressBookDAOSet class 2: // 3: /////////////////////////////////////////////////////////////////// 4: 5: #if !defined(AFX_ADDRESSBOOKDAOSET_H__990F28BF_41F8_11D2_9D79_ 000000000000__INCLUDED_) 6: #define AFX_ADDRESSBOOKDAOSET_H__990F28BF_41F8_11D2_9D79_ 000000000000__INCLUDED_ 7: 8: #if _MSC_VER >= 1000 9: #pragma once 10: #endif // _MSC_VER >= 1000 11: 12: class CAddressBookDAOSet : public CDaoRecordset 13: { 14: public: 15: CAddressBookDAOSet(CDaoDatabase* pDatabase = NULL); 16: DECLARE_DYNAMIC(CAddressBookDAOSet) 17: 18: // Field/Param Data 19: //{{AFX_FIELD(CAddressBookDAOSet, CDaoRecordset) 20: long m_ID; 21: CString m_Last_Name; 22: CString m_First_Name; 23: CString m_Street; 24: CString m_City; 25: CString m_State; 26: long m_Zip; 27: CString m_Phone; 28: //}}AFX_FIELD 29: 30: // Overrides 31: // ClassWizard generated virtual function overrides 32: //{{AFX_VIRTUAL(CAddressBookDAOSet) 33: public: 34: virtual CString GetDefaultDBName(); // REVIEW: Get a comment // here 35: virtual CString GetDefaultSQL(); // default SQL for // Recordset 36: virtual void DoFieldExchange(CDaoFieldExchange* pFX); // RFX support 37: //}}AFX_VIRTUAL 38: 39: // Implementation 40: #ifdef _DEBUG 41: virtual void AssertValid() const; 42: virtual void Dump(CDumpContext& dc) const; 43: #endif 44: 45: }; 46: 47: //{{AFX_INSERT_LOCATION}} 48: // Microsoft Developer Studio will insert additional declarations 49: // immediately before the previous line. 50: 51: #endif // !defined(AFX_ADDRESSBOOKDAOSET_H__990F28BF_41F8_11D2_9D79_ 000000000000__INCLUDED_)
On line 34, notice the declaration of GetDefaultDBName(). This is different from the ODBC configuration. Again, it's the file or data source difference between the two APIs. Other than that difference, the declarations appear identical.
In Listing 15.8, you can explore the document class declaration.
Listing 15.8 The DAO CADDRESSBOOKDAODOC Class Declaration
1: // AddressBookDAODoc.h : interface of the CAddressBookDAODoc class 2: // 3: /////////////////////////////////////////////////////////////////// 4: 5: #if !defined(AFX_ADDRESSBOOKDAODOC_H__990F28BB_41F8_11D2_9D79_ 000000000000__INCLUDED_) 6: #define AFX_ADDRESSBOOKDAODOC_H__990F28BB_41F8_11D2_9D79_ 000000000000__INCLUDED_ 7: 8: #if _MSC_VER >= 1000 9: #pragma once 10: #endif // _MSC_VER >= 1000 11: 12: 13: class CAddressBookDAODoc : public CDocument 14: { 15: protected: // create from serialization only 16: CAddressBookDAODoc(); 17: DECLARE_DYNCREATE(CAddressBookDAODoc) 18: 19: // Attributes 20: public: 21: CAddressBookDAOSet m_addressBookDAOSet; 22: 23: // Operations 24: public: 25: 26: // Overrides 27: // ClassWizard generated virtual function overrides 28: //{{AFX_VIRTUAL(CAddressBookDAODoc) 29: public: 30: virtual BOOL OnNewDocument(); 31: virtual void Serialize(CArchive& ar); 32: //}}AFX_VIRTUAL 33: 34: // Implementation 35: public: 36: virtual ~CAddressBookDAODoc(); 37: #ifdef _DEBUG 38: virtual void AssertValid() const; 39: virtual void Dump(CDumpContext& dc) const; 40: #endif 41: 42: protected: 43: 44: // Generated message map functions 45: protected: 46: //{{AFX_MSG(CAddressBookDAODoc) 47: // NOTE - The ClassWizard will add and remove member functions // here. 48: // DO NOT EDIT what you see in these blocks of generated // code ! 49: //}}AFX_MSG 50: DECLARE_MESSAGE_MAP() 51: }; 52: 53: /////////////////////////////////////////////////////////////////// 54: 55: //{{AFX_INSERT_LOCATION}} 56: // Microsoft Developer Studio will insert additional declarations 57: // immediately before the preceding line. 58: 59: #endif // !defined(AFX_ADDRESSBOOKDAODOC_H__990F28BB_41F8_11D2_9D79_ 000000000000__INCLUDED_)
Notice that this again appears identical to the ODBC MFC Document wrapper class. Notice on line 21 that the document owns the recordset (your coupling).
Finally, the view class is explored in Listing 15.9.
Listing 15.9 The DAO CADDRESSBOOKDAOVIEW Class Declaration
1: // AddressBookDAOView.h : interface of the CAddressBookDAOView // class 2: // 3: /////////////////////////////////////////////////////////////////////// 4: 5: #if !defined(AFX_ADDRESSBOOKDAOVIEW_H__990F28BD_41F8_11D2_9D79_ 000000000000__INCLUDED_) 6: #define AFX_ADDRESSBOOKDAOVIEW_H__990F28BD_41F8_11D2_9D79_ 000000000000__INCLUDED_ 7: 8: #if _MSC_VER >= 1000 9: #pragma once 10: #endif // _MSC_VER >= 1000 11: 12: class CAddressBookDAOSet; 13: 14: class CAddressBookDAOView : public CDaoRecordView 15: { 16: protected: // create from serialization only 17: CAddressBookDAOView(); 18: DECLARE_DYNCREATE(CAddressBookDAOView) 19: 20: public: 21: //{{AFX_DATA(CAddressBookDAOView) 22: enum{ IDD = IDD_ADDRESSBOOKDAO_FORM }; 23: CAddressBookDAOSet* m_pSet; 24: // NOTE: The ClassWizard will add data members here 25: //}}AFX_DATA 26: 27: // Attributes 28: public: 29: CAddressBookDAODoc* GetDocument(); 30: 31: // Operations 32: public: 33: 34: // Overrides 35: // ClassWizard generated virtual function overrides 36: //{{AFX_VIRTUAL(CAddressBookDAOView) 37: public: 38: virtual CDaoRecordset* OnGetRecordset(); 39: virtual BOOL PreCreateWindow(CREATESTRUCT& cs); 40: protected: 41: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support 42: virtual void OnInitialUpdate(); // called first time after // construct 43: virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); 44: virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo); 45: virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo); 46: //}}AFX_VIRTUAL 47: 48: // Implementation 49: public: 50: virtual ~CAddressBookDAOView(); 51: #ifdef _DEBUG 52: virtual void AssertValid() const; 53: virtual void Dump(CDumpContext& dc) const; 54: #endif 55: 56: protected: 57: 58: // Generated message map functions 59: protected: 60: //{{AFX_MSG(CAddressBookDAOView) 61: // NOTE - The ClassWizard will add and remove member functions // here. 62: // DO NOT EDIT what you see in these blocks of generated // code ! 63: //}}AFX_MSG 64: DECLARE_MESSAGE_MAP() 65: }; 66: 67: #ifndef _DEBUG // debug version in AddressBookDAOView.cpp 68: inline CAddressBookDAODoc* CAddressBookDAOView::GetDocument() 69: { return (CAddressBookDAODoc*)m_pDocument; } 70: #endif 71: 72: /////////////////////////////////////////////////////////////////////// 73: 74: AFX_INSERT_LOCATION}} 75: // Microsoft Developer Studio will insert additional declarations 76: // immediately before the preceding line. 77: 78: #endif // !defined(AFX_ADDRESSBOOKDAOVIEW_H__990F28BD_41F8_11D2_9D79_ 000000000000__INCLUDED_)
Again, this looks identical to the ODBC implementation, and in many respects it is.
If you look implementation>at the OnInitialUpdate routine of your view class, you see that it, too, is identical to the ODBC implementation. Do you see a pattern?
The GetDefaultDBName will return a string indicating
the file and path information to the database (see Listing 15.10).
In the ODBC implementation, you had to go to the DSN table to
find this information.
Listing 15.10 DAO GETDEFAULTDBNAME Implementation
1: CString CAddressBookDAOSet::GetDefaultDBName() 2: { 3: return _T("E:\\Teach_Yourself_stuff\\Database_21\\Day 15\\Applications\\AddressBookDB.mdb"); 4: }
TIP |
It's not generally good practice to hard-code string information. Modify this to return a string variable that will contain the file and path declaration. |
The DoFieldExchange, shown in Listing 15.11, is similar
to the DoDataExchange for a dialog-based view. The data
from the database's columns are mapped to the application's memory
by this mechanism.
Listing 15.11 The DAO GETDEFAULTSQL and DOFIELDEXCHANGE
Implementations
1: CString CAddressBookDAOSet::GetDefaultSQL() 2: { 3: return _T("[Addresses]"); 4: } 5: 6: void CAddressBookDAOSet::DoFieldExchange(CDaoFieldExchange* pFX) 7: { 8: //{{AFX_FIELD_MAP(CAddressBookDAOSet) 9: pFX->SetFieldType(CDaoFieldExchange::outputColumn); 10: DFX_Long(pFX, _T("[ID]"), m_ID); 11: DFX_Text(pFX, _T("[Last Name]"), m_Last_Name); 12: DFX_Text(pFX, _T("[First Name]"), m_First_Name); 13: DFX_Text(pFX, _T("[Street]"), m_Street); 14: DFX_Text(pFX, _T("[City]"), m_City); 15: DFX_Text(pFX, _T("[State]"), m_State); 16: DFX_Long(pFX, _T("[Zip]"), m_Zip); 17: DFX_Text(pFX, _T("[Phone]"), m_Phone); 18: //}}AFX_FIELD_MAP 19: }
Both the ODBC and the DAO Address Book applications, as presented, are quite simplistic, and they really don't do much. What if you decided that you need to add another phone number field to the database? You could use Microsoft Access to add the field and rebuild these applications to read in that field. You would also have to place controls on the views to display your new field. This is a little bit of work, but what if you needed to let the application's users define temporary tables and queries? Except for SQL DDL statements, ODBC doesn't give the programmer a method for modifying the data source's schema. Because DAO is file-based, the programmer is able to easily modify the schema programmatically.
MFC provides two class wrappers that enable the programmer to easily modify the structure of the database. These are CDaoTableDef and CDaoQueryDef. These wrappers map to the TableDef and QueryDef objects directly. These classes contain member functions that enable the programmer to build new database structures as well as modify any database structures deemed as updatable.
In many cases, a database schema might be too limited to provide the application with the exact query information that it needs. Instead of doing multiple queries and saving all this information locally within the application for processing, the CDaoQueryDef function member will enable the programmer to add a query definition to the database and then perform a query on the temporary query just created.
If the database is large, temporary tables can be defined for the application that can hold temporary indexes, report results, running totals, and other such items that might be expensive in the application.
TIP |
Although this book isn't intended to instruct the database application developer on programming the older APIs, you would be well adivsed to build a few test applications to solidify your learning. The MFC source code is distributed with the Developer Studio, and it is recommended that the database application programmer review and investigate the wrapper classes. |
After reading this chapter, you will be able to view ODBC and DAO API applications and understand the mechanisms used in ODBC and DAO applications.
MFC AppWizard provides a good start in creating ODBC and DAO appliations. The MFC wrapper classes do much of the work for you as well. The MFC classes automatically perform the data and provide the RFX mechanism. After you define the user interface controls to view the data, your application is off and running. The only code that you have to write is code for reports and other data manipulation.
How do I determine which database support I need? | |
On the MFC AppWizard, the database support without file support is primarily for the ODBC API. Remember that ODBC works with data sources, and DAO works with files. If file support is selected, you are essentially using the Jet database engine. | |
Can I modify the database table by adding fields (columns)? | |
If the application is a DAO application, use the CDaoTableDef class to define a table. The CDaoTableDef has a member function, CanUpdate, that can determine whether field information in the table can be modified. CDaoTableDef can be used to open an existing table or create a new table in a database schema. |
The Workshop quiz questions test your understanding of today's material. (The answers appear in Appendix F, "Answers.") The exercises encourage you to apply the information you learned today to real-life situations.
© Copyright, Sams Publishing. All rights reserved.