Не удается заставить классические параметризованные текстовые запросы ADO работать в устаревшем приложении MFC - PullRequest
1 голос
/ 03 ноября 2011

Я начал обслуживать настольное приложение MFC, которое использует классический ADO.Существует библиотека доступа к базе данных, которая оборачивает импортированный 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.Если я закомментирую 2 вызова функции, ошибки исчезнут, и методы, вызывающие ошибку, не изменятся.

  • Редактировать 21:31 4/11/11 Я должен был дать реализациюClearParameters - см. нижнюю часть приведенного выше кода. Поскольку параметры не были установлены, я имел в виду, что значение, подставляемое в запросе, было «-1», а не «19007» в соответствии с записью SQL Server Profiler. Я думаю, что -1ложное значение, которое возникает из-за того, что параметру не было присвоено значение при его создании.Я должен добавить, что объекты ADO Command & Recordset создаются в конструкторе DBRecordSet.

    • EDIT 21:32 5/11/11 Возможно, я решил проблему,Я использую версию ADO "backcompat", я не уверен, изменились ли типы параметров метода с тех пор, как приложение было написано в VS6, но подпись CreateParameter теперь:

      _bstr_t Name, enum DataTypeEnum Type, enum ParameterDirectionEnum Direction, ADO_LONGPTR Size, const _variant_t & Value
      

, который отличается от параметров в примере кода (Имя - это LPSTR, размер целочисленного параметра в MSDN, например, равен -1, значение передается как long, а не как вариант).в примере с MSDN значение параметра снова устанавливается после создания (хотя не уверен, что это важно, зачем устанавливать его дважды? если это не известная «причуда»).Приведение типов в соответствие с объектной моделью ADO, похоже, добилось цели.Я опубликую это как ответ, если тестирование подтвердит, что все работает правильно

Ответы [ 2 ]

0 голосов
/ 08 ноября 2011

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

РЕДАКТИРОВАТЬ - Пока я заставлял их работать для наборов записей только для чтения,Я обнаружил, что получаю ошибку «Поставщик не поддерживает обновление», когда я использую технику создания соединения и открытия транзакции из этого объекта соединения, и использую это соединение, чтобы открыть несколько последующих наборов записей, изменить различные значения, выдав «Обновление»."Команды и совершение транзакции.Как только я пытаюсь изменить значение поля, я получаю сообщение об ошибке.Это никогда не происходит с открытыми наборами записей (из объекта набора записей, а не с помощью команды) или с сохраненными процессами, создающими набор записей.Расположение соединения - «Клиент», у меня нет оснований ожидать, что оно не будет работать.Я наблюдал это в SQL Profiler, и все выглядит правильно (я думаю) до того момента, пока оно не работает.

0 голосов
/ 04 ноября 2011

Я полагаю, вы просто добавляете параметры каждый раз, когда выполняете параметризованный запрос, а не заменяете существующий параметр.

Как реализован ClearParameters?

Что вы имеете в виду под "параметромне устанавливается (видно в профилировщике SQL) "?Если параметр не был указан, это должно привести к ошибке SQL, не так ли?

...