无法获得经典的ADO参数化文本查询以在旧版MFC应用程序中工作

时间:2011-11-03 13:42:05

标签: ado parameterized-query

我已经开始维护使用经典ADO的MFC桌面应用程序。有一个数据库访问dll包装整个应用程序中使用的导入的ADO。使用存储过程,但也有很多文本查询,其中没有一个是参数化的。我被要求将它们转换为参数化查询。我改变的前2个查询遇到了问题。我正在调用一个函数两次,它从一个名为“Parameters”(无关系)的表中获取一个字符串值,该表由一个唯一的整数索引。我创建一个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的实现 - 参见上面代码的底部 对于未设置的参数,我的意思是根据SQL Server Profiler记录,查询中替换的值为“-1”而不是“19007”。我认为-1是来自参数的伪值在创建时未成功分配值。 我应该补充一下ADO Command& Recordset对象在DBRecordSet构造函数中创建。

    • 编辑 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
      

与示例代码中的参数不同(Name是LPSTR,MSDN中整数参数的大小,例如是-1,值是作为long传递的,而不是变量)也在MSDN示例中参数在Create之后再次设置值(不确定这是否相关,为什么设置它两次?除非这是一个已知的“怪癖”)。使类型符合ADO对象模型似乎已经成功了。  如果测试验证一切正常,我会将此作为答案发布

2 个答案:

答案 0 :(得分:0)

我猜你每次执行参数化查询时都只是添加参数而不是替换现有参数。

如何实施ClearParameters?

你的意思是“参数未被设置(在SQL分析器中看到)”?我没有提供参数,这应该导致SQL错误,不应该吗?

答案 1 :(得分:0)

正如我在上面的编辑中所写,我发现尽管应用程序在引入参数化查询之前工作(即所有存储过程数据访问调用都有效,但隐式转换似乎可以用于SP),正在使用的类型在ADO方法中,“CreateParameter”的参数不正确(长而不是variant_t等)导致“未定义”行为。    一旦我更正了方法参数类型,上面详述的参数化查询(& others)工作正常,&其他看似无关的错误也消失了。    我最初错过了这个,因为该应用程序已经工作了10年+没有参数化查询 - 它们需要添加它来解决问题。 (在接受这个作为答案之前我会等一会儿,以防有人对此有更好的解释)

编辑 - 当我让他们为只读记录集工作时,我发现在使用创建连接和打开连接的技术时,我得到“提供程序不支持更新”错误来自连接对象的事务,并使用此连接打开多个后续记录集,更改各种值,发出“更新”命令并提交事务。一旦我尝试修改字段的值,我就会收到错误。打开记录集(从记录集对象而不是命令)或存储过程创建记录集时,这种情况永远不会发生。连接的位置是“客户端”,我没有理由期望它不起作用。我在SQL事件探查器中观察到它,一切看起来都是正确的(我认为)直到它不起作用。