There are several problems with the original program in the question posted. A modified version with the output produced is provided below.
The first problem was the problem identified by Roman R. above in which the properties for the Open()
were set to allow only reading of rows from the database. As he explains properties needed to be added to allow updating of data.
The OpenAll()
method uses the GetRowsetProperties()
method of the CTable_1Accessor
class to set the properties as a part of processing the open of the database. This can be seen by setting a debugger breakpoint in the method. However other calls to the Open()
method do not invoke the GetRowsetProperties()
function so these other calls to Open()
must have a property set specified in order to modify the default properties for the resulting row set. So not only was a change needed for the GetRowsetProperties()
method of the CTable_1Accessor
class in the wizard generated include file, there also needed to be the addition of the proper properties for each Open()
in order to allow for the use of the SetDate()
method to update the row set.
The second problem with the example posted is that the Close()
function is not being used to close the retrieved rowset after using any of the various Open()
variations. The lack of a Close()
is what was causing the assert when the second and later Open()
were called.
The modified source is below. This was created by starting a new Windows Console project with ATL in Visual Studio 2005 and then using the Class wizard to create the table access ATL based class. With the wizard I checked the boxes in the wizard dialog to allow for Insert, Delete, and Update which then created the proper GetRowsetProperties()
method as described by Roman R. for the Accessor used by the CTable1
class (class CTable_1 : public CCommand<CAccessor<CTable_1Accessor> >
).
The modified example program follows.
#include "stdafx.h"
#include <string>
#include <iostream>
#include "Table_1.h"
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hrResult = OleInitialize(NULL);
switch (hrResult)
{
case S_OK:
break;
default:
std::cout << "Ole Initialization Failed " << hrResult << std::endl;
return 1;
}
// Our standard property set used in various Open() where we specify an SQL query.
CDBPropSet pPropSet(DBPROPSET_ROWSET);
pPropSet.AddProperty(DBPROP_CANFETCHBACKWARDS, true, DBPROPOPTIONS_OPTIONAL);
pPropSet.AddProperty(DBPROP_CANSCROLLBACKWARDS, true, DBPROPOPTIONS_OPTIONAL);
pPropSet.AddProperty(DBPROP_IGetRow, true, DBPROPOPTIONS_OPTIONAL);
pPropSet.AddProperty(DBPROP_IRowsetChange, true, DBPROPOPTIONS_OPTIONAL);
pPropSet.AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_INSERT | DBPROPVAL_UP_DELETE);
CTable_1 myTable; // our table access object we will use for all our operations.
int nItem = 0;
HRESULT hr;
// First example, OpenAll() which uses the default SQL query retrieving all records
hr = myTable.OpenAll ();
for (nItem = 0, hr = myTable.MoveFirst(); hr == S_OK; hr = myTable.MoveNext())
{
char szValueChar[12] = {0};
for (int i = 0; i < 10; i++) szValueChar[i] = (char)myTable.m_IdNumber[i];
std::string sTemp (szValueChar);
std::cout << nItem << " -> " << sTemp << " : " << myTable.m_Count << std::endl;
nItem++;
}
std::cout << " -- Update the first record then redisplay rows" << std::endl;
hr = myTable.MoveFirst (); // move to the first row of the row set
myTable.m_Count++; // increment the count of the first row
hr = myTable.SetData (); // update the database with the modified count
// redisplay all rows including the updated row.
for (nItem = 0, hr = myTable.MoveFirst(); hr == S_OK; hr = myTable.MoveNext())
{
char szValueChar[12] = {0};
for (int i = 0; i < 10; i++) szValueChar[i] = (char)myTable.m_IdNumber[i];
std::string sTemp (szValueChar);
std::cout << nItem << " -> " << sTemp << " : " << myTable.m_Count << std::endl;
nItem++;
}
myTable.Close(); // close the OpenAll() so that we can now do Open() with SQL query.
std::cout << " -- update record specific row" << std::endl;
TCHAR *tsSqlQuery = _T("select * from dbo.Table_1 where IdNumber='0000000002'");
hr = myTable.Open (myTable.m_session, tsSqlQuery, &pPropSet);
if ((hr = myTable.MoveFirst()) == S_OK)
{
char szValueChar[12] = {0};
for (int i = 0; i < 10; i++) szValueChar[i] = (char)myTable.m_IdNumber[i];
std::string sTemp (szValueChar);
LONG countTemp = myTable.m_Count;
myTable.m_Count++;
std::cout << " incrementing " << sTemp << " " << countTemp << " to " << myTable.m_Count << std::endl;
hr = myTable.SetData ();
}
myTable.Close(); // close this row set.
// select all rows and output the values after the various changes
// do an Open() with our new query using our standard property set.
tsSqlQuery = _T("select * from dbo.Table_1");
hr = myTable.Open (myTable.m_session, tsSqlQuery, &pPropSet);
nItem = 0;
for (nItem = 0, hr = myTable.MoveFirst(); hr == S_OK; hr = myTable.MoveNext())
{
char szValueChar[12] = {0};
for (int i = 0; i < 10; i++) szValueChar[i] = (char)myTable.m_IdNumber[i];
std::string sTemp (szValueChar);
std::cout << nItem << " -> " << sTemp << " : " << myTable.m_Count << std::endl;
nItem++;
}
myTable.Close(); // close this row set.
OleUninitialize ();
return 0;
}
The output produced by the above program follows. This output shows the original row values followed by the results of several different changes to some of the row values.
0 -> 0000000001 : 22
1 -> 0000000002 : 12
2 -> 0000000004 : 23
3 -> 0000000006 : 34
-- Update the first record then redisplay rows
0 -> 0000000001 : 23
1 -> 0000000002 : 12
2 -> 0000000004 : 23
3 -> 0000000006 : 34
-- update record specific row
incrementing 0000000002 12 to 13
0 -> 0000000001 : 23
1 -> 0000000002 : 13
2 -> 0000000004 : 23
3 -> 0000000006 : 34