私は最近、いくつかの古いコードでこの問題に遭遇しました。SQL呼び出しのチェーンを動的に構築します(OracleおよびSQL Serverをサポート)。現在のOracle実稼働実装はないため、Oracleの動作をテストした人は誰もおらず、顧客のバグは発生していません。コマンドのチェーンを構築するコードを見つけ、Oracleの場合はを使用しますString.Split(';')
。次に、ループを使用してトランザクション内の各ステートメントを実行します。rowsAffecter += ExecuteNonQuery....
一部のデータにはが含まれる可能性があるため、パラメーター化がないと危険なアプローチになるため、このアイデアは好きではありません;
。しかし、パラメータ化が行われている場合でも...
... Oracle()の匿名ブロックを作成する際の問題の1つは、行数を返さない(-1を返す)ことです。これは、何かが更新されたかどうかを判断するために必要になる場合があります。"begin... end;"
ExecuteNonQuery
この問題を解決するために私はこれをしました
private string AppendOracleCountOrNothing(StringBuilder sql)
{
if (_myProvider == Providers.Oracle)
sql.AppendLine("rowCnt := rowCnt + SQL%ROWCOUNT;");
}
public void SomeMethod()
{
var longSqlChain = new StringBuilder(2000);
longSqlChain.Append("Insert into table...;");
AppendOracleCountOrNothing(longSqlChain);
if (someCondition)
{
longSqlChain.AppendLine("Update anotherTable...;");
AppendOracleCountOrNothing(longSqlChain);
}
// may be, add some more sql to longSqlChain here....
int rowsAffected;
if (_myProvider == Providers.Oracle)
{
longSqlChain.Insert(0, @"DECLARE
rowCnt number(10) := 0
BEGIN
").AppendLine(@":1 := rowCnt;
END;");
// Now, here we have some abstract wrappers that hide provider specific code.
// But the idea is to prepare provider specific output parameter and then parse its value
IDataParameter p = ParameterWrapper.PrepareParameter(":1", 0, ParameterDirection.Output, myProvider); // note IDataParameter
SqlExecWrapper.ExecuteNonQuery(_myProvider, CommandType.Text, sql, new[]{p});
rowsAffected = p.GetParameterValue(); // GetParameterValue is an extension on IDataParameter
}
else // sql server
{
rowsAffected = SqlExecWrapper.ExecuteNonQuery(_myProvider, CommandType.Text, sql, null);
}
}
このようにして、DBに1回アクセスし、この呼び出しの影響を受ける行の戻り数を取得します。クエリもパラメータ化できます。繰り返しになりますが、抽象化レイヤーを開発する方がよいので、のようなものを呼び出すことができますparameterizer.CreateParameter(10)
。これは、コレクションにパラメーターを追加し、SQLステートメントで:1, :2, :3, etc.
(oracle)と@1, @2, @3, etc.
(sql server)を生成します。