従来の ADO を使用する MFC デスクトップ アプリの保守を開始しました。アプリ全体で使用されるインポートされた ADO をラップするデータベース アクセス dll があります。ストアド プロシージャが使用されますが、パラメータ化されていないテキスト クエリも多数あります。それらをパラメーター化されたクエリに変換するように依頼されました。私が変更した最初の 2 つのクエリで問題が発生しました。一意の整数でインデックス付けされた "Parameters" (関係なし) というテーブルから文字列値を取得する関数を 2 回呼び出しています。整数を保持してクエリを実行する ADO パラメータを作成します。これが 2 回目に呼び出されると、パラメーターが設定されず、-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" への 2 つの関数呼び出しの存在または実行のいずれかによって、データ アクセス dll 内の他のメソッドが呼び出されたときに _com_errors (レコードセットのコピーを伴う) をスローすることに気付きました。無関係な dll。2 つの関数呼び出しをコメント アウトすると、エラーがスローされたメソッドに変更がなく、エラーが消えます。
Edit 21:31 4/11/11 ClearParameters の実装を指定する必要がありました - 上記のコードの下部を参照してください パラメータが設定されていない限り、クエリで代入される値は "-1" ではなく "-1" でした。 19007" SQL Server プロファイラーの記録によると、-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
サンプル コードのパラメータとは異なります (名前は LPSTR、MSDN の整数パラメータのサイズは -1、値はバリアントではなく long として渡されます) また、MSDN の例では、パラメータ値が設定されています。 Create の後にもう一度 (これが関連しているかどうかはわかりませんが、これが既知の「癖」でない限り、なぜ 2 回設定するのですか?) 型を ADO オブジェクト モデルに準拠させることで、うまくいったようです。テストですべてが正しく機能することが確認された場合、これを回答として投稿します