PRB: Cannot Create ADO Recordset Hierarchies Manually (202912)



The information in this article applies to:

  • ActiveX Data Objects (ADO) 2.0
  • ActiveX Data Objects (ADO) 2.1
  • ActiveX Data Objects (ADO) 2.5
  • ActiveX Data Objects (ADO) 2.6
  • ActiveX Data Objects (ADO) 2.7

This article was previously published under Q202912

SYMPTOMS

With Microsoft ActiveX Data Objects, you can programmatically create recordsets in memory using the Recordset.Fields.Append method. With the Recordset.Fields.Append method, you can create a field of type adChapter, adDispatch, adVariant, or adUnknown, and then later insert another manually created recordset into this field. This method can be used to manually create a hierarchical (or nested) recordset that appears to work in the same manner as a nested recordset returned by the SHAPE provider. This functionality appears to work properly but is actually not supported by ADO.

CAUSE

The recordset object stored inside of the field is stored as a IDispatch or IUnknown pointer, which works properly in an in-process COM environment. For example, an ADO recordset created in this manner cannot be marshalled over DCOM or HTTP with RDS because ADO does not provide functionality to marshal the internal IDispatch / IUnknown pointers stored inside of the fields. Also, if you close the master recordset without looping through each record and each field and explicitly release each nested recordset, you will leak the memory associated with the child recordset.

STATUS

This behavior is by design. ADO does not support the creation of recordsets in this manner. ADO does not offer marshalling support for the internal recordset pointers. Use of the adChapter, adDispatch, adVariant, and adUnknown fields created using the Recordset.Fields.Append method is not supported by ADO and can result in leaked memory and leaked interface pointers even when used in process.

MORE INFORMATION

The following sample requires advanced knowledge of Remote Data Services (RDS) and ActiveX Data Objects (ADO) programming:
  1. Build a Visual Basic COM DLL with the sample code provided. Make a note of the ProgID of the Visual Basic COM Object (ProgID is "Project Name.Class Name" ).
  2. Add the RDS required ADCLaunch key under:
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Parameters\ADCLaunch
    						

    For additional information about RDS Registry/Security Settings, please see the following article in the Microsoft Knowledge Base:

    191741 INFO: RDS Registry/Security Settings For Custom Business Objects

  3. Run client code from Visual Basic and uncomment one of the three CreateObject calls.
RESULT: Recordset works properly in-process but fails when used with RDS over HTTP and over DCOM.
   ' Start VB COM DLL code
   Function getRS1() As ADODB.Recordset
   Dim rsParent As New ADODB.Recordset
   Dim rsChild As New ADODB.Recordset

      rsParent.CursorLocation = adUseClient
      rsChild.CursorLocation = adUseClient
    
      ' Add fields to recordsets.
      rsParent.Fields.Append "ParentName", adVarChar, 20
      rsParent.Fields.Append "ChildRS", adIDispatch
      rsChild.Fields.Append "ChildName", adVarChar, 20
    
      ' Open recordsets
      rsParent.Open
      rsChild.Open
    
      ' Add records to child recordset.
      rsChild.AddNew
      rsChild(0) = "Child1"
      rsChild.Update
      rsChild.AddNew
      rsChild(0) = "Child2"
      rsChild.Update
      rsChild.MoveFirst
    
      ' Add record to parent recordset.
      rsParent.AddNew
      rsParent(0) = "Parent1"
      rsParent(1) = rsChild
      rsParent.Update
      rsParent.MoveFirst
    
      Set getRS1 = rsParent

   End Function
   ' End VB COM DLL code

   ' Start VB client code
   Private Sub Command1_Click()
   Dim ds As New RDS.DataSpace
   Dim rs As New ADODB.Recordset
   Dim objTestHR As Object

      ' UN-COMMENT ONE OF THE FOLLOWING 3 CREATEOBJECT LINES.
      ' USE THE PROGID FOR THE VB COM DLL CREATED IN STEP 1.

      ' HTTP METHOD:
      'Set objTestHR = ds.CreateObject( "MyProject.MyClass", _               
      '"http://servername" )

      ' DCOM METHOD:
      'Set objTestHR = ds.CreateObject( "MyProject.MyClass", _               
      '"\\servername" )

      ' IN-PROC METHOD:
      'Set objTestHR = CreateObject("MyProject.MyClass")
    
      Set rs = objTestHR.getRS1
      Debug.Print "getRS1 results:"
      Debug.Print ">>" & rs.Fields(0).Value
      Debug.Print ">>>>" & rs.Fields(1).Value.Fields(0).Value
      rs.Fields(1).Value.MoveNext
      Debug.Print ">>>>" & rs.Fields(1).Value.Fields(0).Value
      rs.Close
      Set rs = Nothing
    
   End Sub
   ' End VB client code
				

Modification Type:MajorLast Reviewed:8/23/2001
Keywords:kbDatabase kbDSupport kbprb KB202912