Using MFC Classes with Property Databases

< BACK  NEXT >
[oR]

MFC provides a set of classes that make accessing property databases easier than calling the API function directly. Table 4.23 lists the classes and their purpose.

Table 4.23. MFC property database classes
Class Purpose
CCeDBDatabase Encapsulates a database in the object store. Includes methods for opening, closing, creating, and deleting a database, seeking to records, and reading, writing, and deleting records
CCeDBEnum Enumerates the databases in the object store
CCeDBProp Encapsulates a database property
CCeDBRecord Encapsulates a database record

Since the CCeDBDatabase class does not at present support data volumes, you are limited to creating databases in the object store with these classes.

The best way of using the MFC property database classes is to derive a class from CCeDBDatabase and add methods to your class that reflect how thedatabase will be used. For example, you may create methods such as "AddCustomer", and this in turn calls the CCeDBDatabase method "AddRecord".

Opening and Creating Databases

If you created a new class called CCustomer derived from CCeDBDatabase, you might create a method called Initialize that creates the database if it does not exist, and then opens the database.

 BOOL CCustomer::Initialize() {   CString sDBName(_T("CustomerDB"));   CCeDBProp cePropIndex(CCeDBProp::Type_Long,     enPropCustomerID,     CCeDBProp::Sort_Ascending);   if(!CCeDBDatabase::Exists(sDBName))   {     if(Create(sDBName,         45234,  // database identifier         1,  // number of sort properties         &cePropIndex) == NULL)       return FALSE;   }   // open the database   return Open(sDBName, &cePropIndex, NULL); } 

This code uses a CCeDBProp object called cePropIndex to define the single property to be used as a sort order. The CCeDBProp constructor is passed

  • The data type (using CCeDBProp::Type_Long, which is an enum)

  • The property id (another enum declared in CCustomer defining the properties used for this database)

  • The sort order, CCeDBProp::Sort_Ascending

The CCeDBDatabase method Exists tests whether the database exists, and if it does not, calls CCeDBDatabase::Create to create it. This method is passed

  • The database name

  • The database identifier

  • The number of sort properties

  • A pointer to a CCeDBProp object(s) defining the sort indexes

Finally, the CCeDBDatabase::Open method is called to open the database, and this is passed

  • The name of the database

  • The sort order to be used when opening the database

  • A CWnd pointer for a window that will receive notification messages, or NULL for none

The CCeDBDatabase::Close method can be used to close the database at any time. However, the CCeDBDatabase will close the database automatically. This method takes no parameters.

Reading and Writing Records

Records are added through the CCeDBDatabase::AddRecord method. This is passed a CCeDBRecord object that contains one or more CCeDBProp objects containing the data to be added. The following listing shows a function that will add a single record with two data items, a customer identifier and customer name.

 BOOL CCustomer::AddCustomer(LONG lCustomerID,         CString* sCustomerName) {   CCeDBRecord rec;   CCeDBProp props[2];   props[0] = CCeDBProp(lCustomerID,  enPropCustomerID);   props[1] = CCeDBProp((LPWSTR)(LPCTSTR)*sCustomerName,         enPropCustomerName);   if(!rec.AddProps(props, 2))   {     AfxMessageBox(_T("Could not add properties"));     return FALSE;   }   if(!AddRecord(&rec))   {     AfxMessageBox(_T("Could not add record to DB"));     return FALSE;   }   return TRUE; } 

The array of CCeDBProp objects is initialized by constructing a CCeDBProp object for each data item to be written out to the record. The class CCeDBProp has overloaded constructors for each data type supported by property databases, and is passed the data as the first parameter and the property identifier as the second. Note that since CCeDBProp does not have an operator for CString, two casts are necessary. The first cast, to LPCTSTR, accesses the CString's internal string buffer, and then this is cast to LPWSTR to select the correct CCeDBProp constructor. The class CCeDBProp also contains methods for setting values and property identifiers, such as SetLong and SetString.

The properties in the property array "props" are then added to the CCeDBRecord object through the CCeDBRecord::AddProps method. Finally, therecord is added to the database using the CCeDBDatabase::AddRecord method.

The CCeDBDatabase class maintains a current record pointer, and the following method reads the current record, returning the customer identifier and name.

 BOOL CCustomer::ReadCurrCustomer(LONG* lCustomerID,         CString* sCustomerName) {   CCeDBRecord rec;   CCeDBProp* pPropRecordset;   if(!ReadCurrRecord(&rec))   {     AfxMessageBox(_T("Could not read record"));     return FALSE;   }   pPropRecordset =       rec.GetPropFromIdent(enPropCustomerID);   ASSERT(pPropRecordset != NULL);   *lCustomerID = pPropRecordset->>GetLong();   pPropRecordset =       rec.GetPropFromIdent(enPropCustomerName);   ASSERT(pPropRecordset != NULL);   *sCustomerName = pPropRecordset->>GetString();   return TRUE; } 

The current record is read into a CCeDBRecord object using the CCeDBDatabase::ReadCurrRecord method. The CCeDBRecord::GetPropFromIdent returns a pointer to a CCeDBProp object containing data for the given property identifier. Note that ASSERT is used to test that a non-NULL pointer is actually returned. The data value can be returned from the CCeDBProp object using the appropriate "Get" method for the data type contained in the property. In these two cases GetLong and GetString are used. Note that GetString returns LPWSTR and not a CString.

Seeking to Records

The CCeDBDatabase class supports "Seek" methods to move the current record, including SeekFirst, SeekFirstEqual, SeekLast, SeekNext, SeekNextEqual, SeekPrev, SeekToIndex, SeekToRecord, SeekValueGreater, and SeekValueSmaller. The variable CCeDBDatabase::m_bEOF is used to check for end-of-database. However, the variable is only updated when using SeekNext, or when reading records sequentially with m_bAutoSeekNext set to true (which automatically moves to the next record for each ReadCurrRecord).

Deleting Records and Properties

The CCeDBDatabase::DeleteCurrRecord method deletes the current record, including all the property values associated with the record. This method takes no arguments. The method CCeDBDatabase::DeleteCurrRecordProps deletes one or more properties from the current record. The number of properties to be deleted is passed as the first parameter, and the second parameter contains an array of CCeDBProp properties. These property objects need only contain the property identifiers, not the values themselves.

Serialization and BLOBs

When using MFC you probably use your own C++ class objects to store data, or perhaps the MFC collection classes. If this data needs to be made persistent (that is, stored permanently when your application terminates) you can serialize a C++ class out to a BLOB in a property database. Then, when your application is run again sometime later, the BLOB can be de-serialized, and the C++ classes recreated with the original data. This saves you from having to create a property for each and every piece of data that needs to be saved.

The technique described here will also work if your class contains members that are themselves class objects so long as these class objects also implement serialization. Most of the MFC classes, including the collection classes, support serialization, and so the data associated with these classes will be serialized when your class is serialized. You need to take care that the amount of data in the classes being serialized is no larger than the maximum size of a BLOB in the property database, which is currently 64 KBs.

First, you will need to ensure that your C++ class derives from CObject (the base MFC class), and has serialization enabled. The class declaration should look like the following.

 class CMySerialClass : public CObject { public:   DECLARE_SERIAL(CMySerialClass)   CMySerialClass ();   virtual ~CMySerialClass ();   // implement standard serialization function   void Serialize( CArchive& archive );   // sample class data   UINT nData1, nData2;   // other class methods... }; 

In this case, the class uses the DECLARE_SERIAL macro to implement the necessary class members to support serialization, and declares an override to the Serialize method.

In the implementation file the class will need to implement the Serialize method and also use the IMPLEMENT_SERIAL macro to implement the class members declared in the DECLARE_SERIAL macro.

 IMPLEMENT_SERIAL(CMySerialClass, CObject, 1) void CMySerialClass::Serialize( CArchive& archive ) {   // serialise base clase and class members   CObject::Serialize(archive);   if(archive.IsStoring())   {     archive   nData1;     archive   nData2;   }   else   {     archive >> nData1;     archive >> nData2;   } } 

Serialization of a class operates through a CArchive class object that is normally associated with a file in the file system, or a memory-based file. When serializing to a BLOB you should associate the CArchive with a memory-based file, which is implemented in the MFC class CMemFile. In the following code fragment, a CMemFile class object is declared, followed by a declaration for a CArchive object. The CArchive constructor is passed the CMemFile object (onto which the data will be serialized), followed by a constant, CArchive:: store, that specifies the direction of the archive.

 CMySerialClass* pSerialIt; // created and initialized pSerialIt somewhere pSerialIt = new CMySerialClass(); CMemFile cMemFile; CArchive cArch(&cMemFile, CArchive::store); CEBLOB cBlob; cArch.WriteObject(pSerialIt); cArch.Flush(); cBlob.dwCount = cMemFile.GetLength(); cBlob.lpb = cMemFile.Detach(); props[0] = CCeDBProp(cBlob, PROP_IDENTIFIER); 

The call to CArchive::WriteObject requests the object pointed to by pSerialIt to archive itself onto the archive object. This results in the CMySerialClass::Serialize method shown above being called. The CArchive::Flush method is called to ensure that all outstanding input/ output requests have been completed.

The CMemFile.GetLength method returns the number of bytes that have been serialized, and this is copied into the CEBLOB dwCount member. The CMemFile.Detach method returns a memory pointer which is copied into the CEBLOB lpb member. A database property can then be created using the CCeDBProp constructor, which is described earlier in the section "Reading and Writing Records."

In conclusion, this code will result in all the data associated with the class CMySerialClass being saved to a property in a record in a property database.

The reverse process must be performed to deserialize the data back into a CMySerialClass object. In the next code fragement, the CEBLOB is obtained from a CCeDBRecord object in the usual way. A CMemFile object is declared, and the Attach method is used to attach the data pointed to by the CEBLOB lpb member. Once this is done, an CArchive object is created, passing the CMemFile object as the first parameter (which is the source data for the archive) and the direction (loading data in this case) using the constant CArchive::load. The CArchive method ReadObject will deserialize the object, creating a new CMySerialClass object, calling the CMySerialClass::Serialize method described above, and returning the CMySerialClass object pointer.

 CMySerialClass* pSerialIt; CEBLOB rsBlob; rsBlob = pPropRecordset->>GetBlob(); CMemFile cMemFile; cMemFile.Attach(rsBlob.lpb, rsBlob.dwCount); CArchive cArch(&cMemFile, CArchive::load); pSerialIt = (CDBQueueError*) cArch.ReadObject(pClass); 

< BACK  NEXT >


Windows CE 3. 0 Application Programming
Windows CE 3.0: Application Programming (Prentice Hall Series on Microsoft Technologies)
ISBN: 0130255920
EAN: 2147483647
Year: 2002
Pages: 181

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net