Introduction
ATK can retrieve ActiveX objects from an ObjectStore database by means of the ObjectStore Active Toolkit OLE DB provider. You can access OLE DB providers directly, or through active data objects (ADO). ObjectStore Active Toolkit OLE DB also lets you use an ODBC data source, query with SQL syntax, and perform create, read, update, and delete operations using user-defined methods. In this chapter
This chapter presents the following topics:
You can access OLE DB providers through ActiveX Data Objects (ADO). ADO is a high-level object model that provides access to any OLE DB source. The ADO interface is similar to the Data Access Objects (DAO) interface and the Remote Data Objects (RDO) interface.
To obtain a complete description of the ADO object model, consult the on-line documentation for Microsoft Visual Basic, Active Server Pages, or Visual C++.
For more information about OLE DB and ADO, browse the Microsoft web site at http://www.microsoft.com/data.
Navigation in ObjectStore ATK OLE DB
Navigating between objects is a common technique for exposing a tabular representation of a class hierarchy. For example, suppose an ObjectStore database contains the two classes Customer and Vehicle. The Customer class has a relationship that lists each Vehicle owned by a Customer and each Vehicle is linked to its Customer. (The sample database carsdemo.db contains this kind of schema.)
Implicit navigation
Using this collection of Customers, you can build a data view in the Inspector that displays the name of each Customer, and the make and model of each Vehicle he or she owns.
Response.Write("<TABLE BORDER=1>") For Each colHeader in RS.fields Response.Write("<TH><B><PRE>" & colHeader.Name & "</PRE></B></TH>") Next Do While Not RS.EOF Response.Write("<TR>") For Each aField in RS.fields Response.Write("<TD>" & aField.Value & "</TD>") Next Response.Write("</TR>") RS.MoveNext Loop Response.Write("</TABLE>")
ADO 1.5 and later supports chaptered row sets, which provide a standard way to nest record sets. In this case the Field.Value item is a RecordSet object and you can use it to display the navigated elements.
The resulting HTML table looks like this:
Clicking on an item in the cars column explicitly navigates the Customer to Vehicle relation:
To navigate explicitly in ObjectStore Active Toolkit OLE DB, you have to write additional code to handle Field.Value. If you are using ADO 1.5 or later, ATK handles explicit navigation using the chaptered row set functionality. From the user standpoint, this means that the Field.Value is registered as type (Field.Type) 136 and that it is an ADO RecordSet object that can be used to display the navigated rows.
(To learn about specifying explicit navigation, see SQL Support in ATK OLE DB.)
Sample code ADO 2.0
This sample code provides explicit navigation:
For Each aField in RS.fields If aField.Type=136 Then 'Is it a Chaptered Rowset? set navigatedRS=aField.Value \QnavigatedRS is an ADO RecordSet object else Response.Write(aField.Value) end if NextAn OLE DB consumer (that is, a consumer that directly accesses OLE DB, without using ADO) can detect explicit navigation by checking the type of the column.
If the column is registered as DBTYPE_IDISPATCH, the column contains a pointer to an IDispatch interface that exposes the GetCardinality, GetRecordSet and GetIRowset methods. The OLE DB consumer can then access these methods to retrieve the navigated table.
If the column is registered as DBTYPE_HCHAPTER, the column contains a handle to the chapter (HCHAPTER). Refer to the Microsoft OLE DB documentation for information on how to handle chaptered row sets.
osmmData=RS(0).GetChunk(RS(0).ActualSize)Then use osmmData to access an IStream or a SAFEARRAY OLE interface. For example, in ASP, use osmmData in conjunction with the BinaryWrite Response object method:
Response.BinaryWrite(osmmData)The example WebADODemo shows how to display a collection of multimedia Object Managers using the ObjectStore Active Toolkit OLE DB provider. This HTML page displays a collection of images stored in the ObjectStore database as osmmImage objects and retrieved using the ObjectStore Active Toolkit OLE DB provider:
Opening a connection
In general, to open an ObjectStore Active Toolkit OLE DB connection, provide a string in this format:
provider=ObjectStore Active Toolkit OLE DB Provider; data source=< full-db-path>;where
[Join=[SQL|Inspector];] [Import=< import-database>;]
Name | Make | Model |
---|---|---|
John, Smith | Ford | Escort |
Cadillac | DeVille | |
Bob, Dylan | Ford | Ranger |
Mazda | 626 | |
Dodge | Spirit |
SQL relationships
If you specify Join=SQL, ATK expands one-to-many relationships to show all the possible values that meet the conditions of the query (as an SQL join would). The output takes this form:
Name | Make | Model |
---|---|---|
John, Smith | Cadillac | Deville |
Bob, Dylan | Ford | Ranger |
Bob, Dylan | Mazda | 626 |
Bob, Dylan | Mazda | 626 |
Bob, Dylan | Dodge | Spirit |
Choosing between the two
Usually a tree-like view is more readable, but an SQL-oriented tool such as a report generator cannot easily use it. However, the SQL-like view is less readable and, if you navigate multiple one-to-many relationships in a row, the number of generated rows is much greater than in the tree-like view.
Set adoConnection = Server.CreateObject("ADODB.Connection") Call adoConnection.Open(" provider=ObjectStore Active Toolkit OLE DB Provider; data source=c:\odi\ATK6.0\Examples\demodbs\carsdemo.db")Then you can open a RecordSet:
Set RS = Server.CreateObject("ADODB.RecordSet") Call RS.Open("myDataView", adoConnection)where myDataView is the name of a data view in the ATK metaknowledge of the current database, or a SQL query following the syntax accepted by ObjectStore Active Toolkit OLE DB. (For more information, see SQL Support in ATK OLE DB.)
Providing user and password values
ObjectStore Active Toolkit OLE DB can also obtain information about the user and password values to open the ObjectStore database. You can provide this kind of information in two ways:
Call adoConnection.Open(" provider=ObjectStore Active Toolkit OLE DB Provider; data source=c:\odi\ATK6.0\Examples\demodbs\carsdemo.db", "userName", "password")
Through the OLE DB interface, set the DBPROP_INIT_PROMPT property in OLE DB to a value different from DBPROMPT_NOPROMPT.
If ATK cannot get the user name and password information it requires (because it is not specified when the connection is opened and ATK is not enabled to prompt the user), an error condition is generated.
Accessing the ATK OLE DB Source Through ODBC
OLE DB providers and ODBC drivers provide similar services, making it possible to build a bridge between any OLE DB provider and ODBC. Use ISG Navigator/Bridge
ISG Navigator/Bridge includes an ODBC driver for OLE DB data sources. It consumes OLE DB providers and allows ODBC applications to access any OLE DB data source. ISG Navigator/Bridge is distributed by ISG International Software Group at http://www.isgsoft.com, and by Microsoft Corporation at http://www.microsoft.com.
Download and install ISG Navigator/Bridge. Then you can configure it to access ObjectStore Active Toolkit OLE DB through any ODBC client. Refer to Chapter 5, Using Crystal Reports with ATK, in the ObjectStore Active Toolkit Tutorial for an example of configuring ISG Navigator/Bridge.
SQL Support in ATK OLE DB
ATK supports a subset of the SQL syntax. This SQL support lets you dynamically modify data views defined in Inspector by customizing the instance format and the filter, and by ordering definitions. ATK supports these statements:
CRUD Operations in ATK OLE DB
You can use the Active Toolkit OLE DB interface to perform CRUD operations (create, read, update, and delete) on objects in an ObjectStore database. This section describes the ways you can implement CRUD operations using ATK. Prerequisite
You should understand how to work with user-defined methods before trying to implement CRUD operations in ATK. Information about creating, registering, and using user-defined methods is described in Chapter 7, User-Defined Methods, of the ObjectStore Inspector User Guide.
Ways to Implement CRUD Operations
You can perform CRUD operations in ADO clients using either
Note: Data member names can also be modified using the SQL ALIAS command.
To create, update, or delete persistent instances of CAuthor from an ADO client, you would first retrieve a table of authors by execute a statement like this one:
adoRS.OPEN( "SELECT m_FirstName, m_Books#m_ISBN FROM CAuthor")The resulting table would look like this:
Next, the client would edit the table, for example, adding a new author:
adoRS.AddNew "m_FirstName" , "Ugo"The AddNew command calls a C++ user-defined method registered in Inspector, CAuthor::oledb_create, to create a new instance of CAuthor. You can learn more about the details of this method in Creating Objects.
Editing relationships
You can also edit relationships themselves. Note that the CAuthor and CBook classes are related, in ObjectStore, by the m_Books and m_Authors data members.
adoRS.OPEN( "SELECT m_FirstName, {SELECT m_ISBN FROM m_Books} FROM CAuthor")Here is the resulting table:
Notice that the SELECT statement creates chaptered rowsets, that is, tables within tables. It is against these chaptered rowsets that you executing statements in order to edit a relationship.
Next, you locate the chaptered rowset for a given instance, in this case the first instance, for the author Owen.
adoRS.MoveFirstNext, you retrieve the chaptered rowset for the author:
SET owenBooks= adoRS("m_Books")Finally, you execute the desired CRUD statement, such as adding a new book for that author:
owenBooks.AddNew "m_ISBN", "1-879239-06-06"The result here is that a new book (identified by its ISBN) is added to the relationship identified by the selected author, in this case, Owen. As before, the AddNew command calls a C++ user-defined method registered in Inspector. This method requires a different signature because it is used to edit a relationship, not an object: CAuthor::oledb_create_relationship
static PersistentClass* PersistentClass::oledb_create(
os_database* aDB, const os_Dictionary<char*, void*>& args)
The fields you specify in the AddNew method must be the same as (or a subset of) the data members exposed through the instance format of the data view corresponding to the RecordSet and the arguments specified in the registered oledb_create user-defined method.
INSERT INTO CCustomer (m_FirstName, m_LastName) VALUES ('Hank', 'Foster')
int PersistentClass::oledb_update( const os_Dictionary<char*, void*>& args)
The fields you modify must be the same as (or a subset of) the data members exposed through the instance format of the data view corresponding to the RecordSet and the arguments specified in the registered oledb_update user-defined method.
UPDATE CCustomer SET m_FirstName='Henry' WHERE m_FirstName='Hank' AND m_LastName='Foster'
int PersistentClass::oledb_create_relationship( const os_Dictionary<char*, void*>& args)This method must be able to update a relationship of the PersistentClass on which it is defined. This method always receives an argument, oledb_relationship_name, which identifies the relationship that is to be updated. The other arguments received by oledb_create_relationship are the parameters specified by the user in the ADO command that invoked the method. These parameters must be sufficient to uniquely identify the other object participating in the relationship.
From ADO you use chaptered rowsets to represent relationships in OLE DB tables. You can invoke the oledb_create_relationship by calling the RecordSet::AddNew method on a RecordSet obtained by an OLE DB chaptered rowset.
For example, if you open a RecordSet with the SQL command
SELECT m_FirstName, {SELECT m_OrderID, m_OrderDescription FROM m_Orders} FROM CCustomerthe second column identifies a chaptered rowset. This means that the fields of the RecordSet corresponding to the second column are RecordSet objects themselves; each contains a description of all the orders made by a customer.
If you call RecordSet::AddNew on the navigated RecordSet that contains the order information, ATK OLE DB tries to invoke CCustomer::oledb_create_relationship, specifying m_Orders in the oledb_relationship_name argument and passing the other arguments specified in the RecordSet::AddNew method.
static int PersistentClass::oledb_delete( void* anObject)
DELETE FROM CCustomer WHERE m_FirstName='Henry' AND m_LastName='Foster'
int PersistentClass::oledb_delete_relationship( const os_Dictionary<char*, void*>& args)This method must be able to delete a relationship of the PersistentClass on which it is defined. This method always receives an argument, oledb_relationship_name, which identifies the relationship that is to be deleted. The other arguments received by oledb_delete_relationship are provided by ATK - they are the intersection of all the chaptered rowset columns and the arguments registered in the oledb_delete_relationship method.
From ADO you use chaptered rowsets to represent relationships in OLE DB tables. You can invoke the oledb_delete_relationship by calling the RecordSet::Delete method on a RecordSet obtained by an OLE DB chaptered rowset.
For example, if you open a RecordSet with the SQL command
SELECT m_FirstName, {SELECT m_OrderID, m_OrderDescription FROM m_Orders} FROM CCustomerthe second column identifies the chaptered rowset. This means that the fields of the RecordSet corresponding to the second column are RecordSet objects themselves; each contains a description of all the orders made by a customer.
If you position on order number 23, the myOrderedBook row of the navigated RecordSet, and then call RecordSet::Delete, ATK OLE DB tries to invoke CCustomer::oledb_delete_relationship, specifying m_Orders in the oledb_relationship_name argument, 23 in the m_OrderID argument, and myOrderedBook in the m_OrderDescription argument.
For example, this code attempts to open a database in an Active Server Page:
On Error Resume Next Set adoConnection = Server.CreateObject("ADODB.Connection") Call adoConnection.Open("provider=ObjectStore OLE DB Provider;data source=mydb.db","","") if Err.Number <> 0 then Response.Write("Error# " & Hex(Err.Number) & "<BR>") Response.Write("Generated by: " & Err.Source & "<BR>") Response.Write("Description: " & Err.Description & "<BR>") Exit Sub End IfIf the database is not available, the Active Server Page generates this output:
Error# 80041000 Generated by: ObjectStore OLE DB Provider Description: Could not open in MVCC mode the database 'mydb.db'!You might find it useful to scan the Connection::Errors collection to retrieve multiple descriptions for the error. (Refer to Microsoft's ADO documentation for further information about the Connection::Errors collection.)
Updated: 05/11/99 11:35:03