OLEDB - Disconnected Recordsets

If you are going to use the client cursor engine then often it’s a good idea to disconnect your recordset…

Disconnected Recordsets

The normal procedure for using the Client Cursor Engine is to open your recordset with client side cursors and then disconnect it from the data source. This causes the CCE to pull all of your data out of the data source and effectively marshal it by value to your client. Though the code available so far allows the use of the CCE there’s no way to disconnect the recordset from your simple data object. We’ll address that issue now.

What happens when we disconnect?

When you disconnect a recordset by setting the active connection to nothing what happens inside your provider is that the Cancel method is called on your command object. In fact, the Cancel method is always called when the consumer is done with the underlying data source. Thus, we can intercept the call to Cancel and use it as a trigger to release our connection to the data object. To be able to do this we need to be able to communicate between the Command object and the rowset that it creates. To achieve this communication we can add a new interface to our proxy rowset:

interface IDisconnectableRowset : IUnknown
{
   HRESULT DisconnectRowset();
};

We can then query for this interface from our Command object as the last thing we do inside of the Execute method.

if (SUCCEEDED(hr))
{
   hr = pGetAsRowset->GetAsRowset(
      pOurUnknown,
      pUnkOuter,
      riid,
      pcRowsAffected,
      ppRowset);

   pOurUnknown->Release();
}

if (SUCCEEDED(hr))
{
   hr = (*ppRowset)->QueryInterface(
      IID_IDisconnectableRowset,
      (void**)&m_pRowset);
}

pGetAsRowset->Release();

The Command object now has a way to tell the rowset that it has been disconnected, so we can add an implementation of ICommand::Cancel() that does that.

HRESULT CConversionProviderCommand::Cancel()
{
   HRESULT hr = ICommandTextImpl<CConversionProviderCommand>::Cancel();

   ATLTRACE2(atlTraceDBProvider, 0, "CConversionProviderCommand::Cancel\n");

   if (SUCCEEDED(hr))
   {
      if (m_pRowset)
      {
         hr = m_pRowset->DisconnectRowset();
         m_pRowset->Release();
         m_pRowset = 0;
      }
   }

   return hr;
}

First calling our base class to see if we can be cancelled and if so performing the disconnection on the rowset object.

What does the Rowset do?

I chose to make all rowsets derived from CProxyRowsetImpl be disconnectable, so that’s where the implementation of IDisconnectableRowset::DisconnectRowset() lives. It’s fairly simple, it just causes the rowset to release the data object that it’s a proxy rowset for. If nobody else has a reference to the data object at that point then it will cease to exist, that seems pretty disconnected to me…

template <
   class DataClass,
   class T,
   class CreatorClass,
   class Storage,
   class ArrayType,
   class RowClass,
   class RowsetInterface>
HRESULT CProxyRowsetImpl<
   DataClass,
   T,
   CreatorClass,
   Storage,
   ArrayType,
   RowClass,
   RowsetInterface>::DisconnectRowset()
{
   ObjectLock lock(this);

   if (m_pDataObject)
   {
      m_pDataObject = 0;
   }

   if (m_pUnkDataObject)
   {
      m_pUnkDataObject->Release();
      m_pUnkDataObject = 0;
   }

   return S_OK;
}

That’s all there is to it. We now have a disconnectable rowset.

Download

The following source built using Visual Studio 6.0 SP3. Using the July 2000 edition of the Platform SDK. If you don’t have the Platform SDK installed then you may find that the compile will fail looking for “msado15.h”. You can fix this problem by creating a file of that name that includes “adoint.h”.

If your system drive isn’t D:\ then you’ll have to change the #import statements in IGetAsADORecordsetImpl.h and IGetAsADORecordset.cpp.

Revision history

  • 21st May 2000 - Initial revision at www.jetbyte.com.
  • 2nd October 2000 - Fixed some build configuration errors. Thanks to Charles Finley for reporting these.
  • 9th February 2001 - Added the C++ client example.
  • 19th October 2005 - reprinted at www.lenholgate.com.

Other articles in the series

  • Objects via ADO - ADO seems to be the ideal way to expose tabular data from your own COM objects and the ATL OLE DB Provider templates can help!
  • Custom Rowsets - The ATL OLE DB Provider templates appear to rely on the fact that your data is kept in a simple array, but that’s not really the case at all!
  • IRowsetLocate and Bookmarks - Adding bookmark functionality is relatively easy and it enables our ADO recordset to be used with a greater number of data bound controls.
  • Updating data through an ADO recordset - The ATL OLE DB Provider templates only seem to support read-only rowsets, and making them support updating of data isn’t as easy as you’d expect!
  • Client Cursor Engine updates - Making the ADO Client Cursor Engine believe that your rowset is updateable involves jumping through a few extra hoops…
  • Disconnected Recordsets - If you are going to use the client cursor engine then often it’s a good idea to disconnect your recordset…