Я начал поддерживать настольное приложение MFC, использующее классический ADO. Существует dll для доступа к базе данных, которая обертывает импортированный ADO, который используется во всем приложении. Используются хранимые процедуры, но также много текстовых запросов, ни один из которых не параметризован. Меня попросили преобразовать их в параметризованные запросы. Первые 2 запроса, которые я изменил, столкнулись с проблемой. Я дважды вызываю функцию, которая получает строковое значение из таблицы под названием «Параметры» (без связи), индексированной уникальным целым числом. Я создаю параметр ADO для хранения целого числа и выполнения запроса. Во второй раз, когда это вызывается, параметр не устанавливается и возвращает -1 (видно в профилировщике SQL, в первый раз это правильно), что приводит к пустому набору записей. Я затрудняюсь объяснить это (хотя из прошлого опыта я упускаю что-то очевидное), кажется вероятным объяснением является то, что код создания или очистки параметров, который я унаследовал, неверен (но я, вероятно, ошибаюсь насчет это тоже :-) - вот пример кода:
m_spGlobal->LoginType = CSTR GetParam(19006); //Calls function below
BOOL bPrompt = (GetParam(19007) == "1") ? TRUE:FALSE; //Second call
CString CMainFrame::GetParam(int nPram )
{
DBSWIFTLib::IDBRecordsetPtr pDB(__uuidof(DBSWIFTLib::DBRecordset));//Declare smart pointer to dB access dll
CString sSQL;
pDB->ClearParameters();// loops through parameters collection, deleting any that exist(but shouldn't be any)
pDB->ParameterInt( "pPram",nPram, ADODB::adParamInput);
pDB->LockType = ADODB::adLockReadOnly;
pDB->CursorLocation = ADODB::adUseClient;
pDB->CursorType = ADODB::adOpenForwardOnly;
sSQL.Format("SELECT sValue FROM Parameters WITH (NOLOCK) WHERE Id= ?");
pDB->ExecuteSQL(CSTR sSQL);
CString sReturn = "";
if (!pDB->Empty())
{
sReturn = pDB->strval["sValue"];
}
return sReturn.Trim();
}
// ExecuteSQL from above function(in different COM dll)
STDMETHODIMP CDBRecordset::ExecuteSQL(LPSTR pszCommand)
{
try
{
if (m_pRs->State != adStateClosed )
m_pRs->Close( );
m_pCmd->CommandText = pszCommand;
m_pCmd->CommandType = adCmdText;
m_pCmd->PutActiveConnection(_variant_t((IDispatch* )m_pRsConn ) );
m_pRs = m_pCmd->Execute(&vtMissing, &vtMissing, adCmdText );
}
catch (_com_error &e)
{
m_strFunction = _T("ExecuteSQL");
m_strExtraInfo = pszCommand;
DisplayADOError(e );
}
return S_OK;
}
//In same dll as previous function
STDMETHODIMP CDBRecordset::ParameterInt(LPSTR pszName, int nValue, int nDirection )
{
try
{
m_pParam = m_pCmd->CreateParameter(pszName,
adInteger,
(ParameterDirectionEnum )nDirection,
sizeof(nValue ),
(long )nValue );
m_pCmd->Parameters->Append(m_pParam );
}
catch (_com_error &e )
{
m_strFunction = _T("ParameterInt");
m_strExtraInfo = pszName;
DisplayADOError(e );
}
return S_OK;
}
STDMETHODIMP CDBRecordset::ClearParameters( )
{
try
{
long cParams = (m_pCmd->Parameters->Count - 1 );
for (long m_nParams = cParams; m_nParams >= 0; m_nParams-- )
m_pCmd->Parameters->Delete(variant_t(m_nParams ) );
}
catch (_com_error &e )
{
m_strFunction = _T("ClearParameters");
DisplayADOError(e );
}
return S_OK;
}
Дополнительная (странная) информация: теперь я заметил, что либо существование, либо выполнение двух вызовов функций «GetParams» приводят к тому, что некоторые другие методы в dll доступа к данным вызывают _com_errors (они включают копирование набора записей), когда они вызываются из несвязанная dll. Если я закомментирую вызовы двух функций, ошибки исчезнут без каких-либо изменений в методах, вызывающих ошибку.
Изменить 21:31 11.04.11 Я должен был указать реализацию ClearParameters - см. нижнюю часть кода выше. Поскольку параметры не установлены, я имел в виду, что значение, подставленное в запросе, было "-1", а не " 19007», согласно записи SQL Server Profiler. Я думаю, что -1 является ложным значением, которое происходит из-за того, что параметру не было успешно присвоено значение при его создании. Я должен добавить, что объекты ADO Command & Recordset создаются в конструкторе DBRecordSet.
EDIT 21:32 11.05.11 Возможно, я решил проблему. Я использую версию ADO «backcompat», я не уверен, изменились ли типы параметров метода с тех пор, как приложение было написано в VS6, но подпись CreateParameter теперь:
_bstr_t Name, enum DataTypeEnum Type, enum ParameterDirectionEnum Direction, ADO_LONGPTR Size, const _variant_t & Value
который отличается от параметров в примере кода (имя - это LPSTR, размер для целочисленного параметра в MSDN, например, -1, значение передается как длинное, а не вариант) Также в примере MSDN значение параметра установлено снова после создания (хотя не уверен, что это актуально, зачем устанавливать его дважды? если это не известная «причуда»). Приведение типов в соответствие с объектной моделью ADO, похоже, помогло. Я опубликую это как ответ, если тестирование подтвердит, что все работает правильно