Не удается заставить классические параметризованные текстовые запросы ADO работать в устаревшем приложении MFC

Я начал поддерживать настольное приложение 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, похоже, помогло. Я опубликую это как ответ, если тестирование подтвердит, что все работает правильно


person andywebsdale    schedule 03.11.2011    source источник


Ответы (1)


Как я писал в разделе «Редактировать» выше, я обнаружил, что, хотя приложение работало до введения параметризованных запросов (т. е. все вызовы доступа к хранимым процедурам работали, неявные приведения, по-видимому, в порядке для SP), типы, используемые в ADO параметры метода для «CreateParameter» были неправильными (long вместо variant_t и т. д.), что приводило к «неопределенному» поведению. Как только я исправил типы параметров метода, параметризованные запросы, описанные выше (и другие), работали нормально, а другие, казалось бы, несвязанные ошибки также исчезли. Сначала я пропустил это, потому что приложение работало более 10 лет без параметризованных запросов — потребовалось их добавление, чтобы выявить проблему. (Я немного подожду, прежде чем принять это как ответ, на случай, если у кого-то есть лучшее объяснение этого)

EDIT. Хотя я заставил их работать с наборами записей только для чтения, я обнаружил, что получаю ошибку «поставщик не поддерживает обновление» при использовании метода создания соединения и открытия транзакции из объект соединения и использовать это соединение для открытия нескольких последующих наборов записей, изменения различных значений, выдачи команд «Обновить» и фиксации транзакции. Как только я пытаюсь изменить значение поля, я получаю сообщение об ошибке. Это никогда не происходит с открытыми наборами записей (из объекта набора записей, а не из команды) или хранимыми процедурами, создающими набор записей. Расположение соединения — «Клиент», у меня нет причин ожидать, что оно не сработает. Я наблюдал это в SQL Profiler, и все выглядит правильно (я думаю) до того момента, когда оно не работает.

person andywebsdale    schedule 07.11.2011