21

これらの問題を抱えたのは私が初めてではなく、以下にいくつかの参考記事をリストしますが、まだ適切な解決策を探しています.

C# Web サービスからストアド プロシージャ (Oracle 10g データベース) を呼び出す必要があります。Web サーバーには Oracle 9i クライアントがインストールされており、Microsoft を使用していますSystem.Data.OracleClient

このプロシージャは、XML を CLOB として受け取ります。XML が 4000 バイトを超えたとき(これは通常の使用例です)、次のエラーに遭遇しました。

ORA-01460 - 実装されていない、または不当な変換が要求されました

これこれ、およびこの投稿を見つけました。

さらに、ストアド プロシージャを C# から直接呼び出すのではなく、匿名の PL/SQL コードを定義する有望な回避策を見つけました。このコードは OracleCommand として実行されます。XML は文字列リテラルとして埋め込まれ、プロシージャ コールはそのコード内から実行されます。

private const string LoadXml =
    "DECLARE " +
    "  MyXML CLOB; " +
    "  iStatus INTEGER; " +
    "  sErrMessage VARCHAR2(2000); " +
    "BEGIN " +
    "  MyXML := '{0}'; " +
    "  iStatus := LoadXML(MyXML, sErrMessage); " +
    "  DBMS_OUTPUT.ENABLE(buffer_size => NULL); " +
    "  DBMS_OUTPUT.PUT_LINE(iStatus || ',' || sErrMessage); " +
    "END;";
OracleCommand oraCommand = new OracleCommand(
    string.Format(LoadXml, xml), oraConnection);
oraCommand.ExecuteNonQuery();

残念ながら、このアプローチは、XML が 32 KB 程度を超えるとすぐに失敗するようになりました。今回のエラーは、次の PL/SQL コンパイラに起因します。

ORA-06550: 行1、列87: PLS-00172: 文字列リテラルが長すぎます

いくつかの調査の後、2番目のアプローチで問題を解決することは不可能であると結論付けました。

上記の投稿に続いて、次の2つのオプションがあります。

(最初の投稿では、一部のクライアントにはバグがあると記載されていましたが、私の (9i) は前述の 10g/11g バージョンの範囲には含まれていません。)

これらの 2 つのオプションしか残っていないことを確認できますか? または、私を助ける別の方法はありますか?

明確にするために: XML最終的にどのテーブルにも保存されませんが、XML の内容に基づいていくつかのテーブルにいくつかのレコードを挿入するストアド プロシージャによって処理されます。

2つのオプションに関する私の考慮事項:

  • ODP.NET への切り替えは困難です。これまでのところ、システムにアクセスできない Web サーバーに ODP.NET をインストールする必要があり、クライアントにもコードをデプロイする必要があるためです。デプロイメントの一部として ODP.NET をインストールします。
  • テーブルを迂回すると、クライアント コードがかなり複雑になり、PL/SQL ルーチンを適応/拡張するデータベースにかなりの労力がかかります。
4

4 に答える 4

15

この問題を回避する別の方法があることがわかりました。私の仲間の従業員は、このブログを教えてくれて、私の一日を救ってくれました。

DbConnection で BeginTransaction が既に呼び出されている場合は、パラメーター値を設定します。

もっと簡単にできますか?このブログは に関連してOracle.DataAccessいますが、 にも同様に機能しSystem.Data.OracleClientます。

実際には、これは次のことを意味します。

varcmd = new OracleCommand("LoadXML", _oracleConnection);
cmd.CommandType = CommandType.StoredProcedure;

var xmlParam = new OracleParameter("XMLFile", OracleType.Clob);
cmd.Parameters.Add(xmlParam);

// DO NOT assign the parameter value yet in this place

cmd.Transaction = _oracleConnection.BeginTransaction();
try
{
    // Assign value here, AFTER starting the TX
    xmlParam.Value = xmlWithWayMoreThan4000Characters;

    cmd.ExecuteNonQuery();
    cmd.Transaction.Commit();
}
catch (OracleException)
{
    cmd.Transaction.Rollback();
}
于 2010-08-25T14:11:53.993 に答える
3

私の場合、chiccodoro のソリューションは機能しませんでした。ODP.NET ( Oracle.DataAccess) を使用しています。

私にとっての解決策は、OracleClobオブジェクトを使用することです。

OracleCommand cmd = new OracleCommand("LoadXML", _oracleConnection);
cmd.CommandType = CommandType.StoredProcedure;

OracleParameter xmlParam = new OracleParameter("XMLFile", OracleType.Clob);
cmd.Parameters.Add(xmlParam);

//connection should be open!
OracleClob clob = new OracleClob(_oracleConnection);
// xmlData: a string with way more than 4000 chars
clob.Write(xmlData.ToArray(),0,xmlData.Length);
xmlParam.Value = clob; 

try
{
    cmd.ExecuteNonQuery();
}
catch (OracleException e)
{
}
于 2013-07-08T09:25:17.420 に答える
1

安いポイントを得るためにこれをグーグルで検索しただけだと思いますが、ここに素晴らしい説明があります:

http://www.orafaq.com/forum/t/48485/0/

基本的に、文字列リテラルで 4000 文字を超える文字を使用することはできません。それ以上の文字数を使用する必要がある場合は、ストアド プロシージャを使用する必要があります。次に、最大で 32KB に制限されるため、挿入を「チャンク」する必要があります。ブリーチ。

-オイシン

于 2010-08-24T17:52:13.343 に答える
1

チッコドロ そうです。

public static int RunProcedure(string storedProcName, IDataParameter[] parameters)
    {
        using (OracleConnection connection = new OracleConnection(connectionString))
        {
            int rowsAffected;

            OracleCommand command = new OracleCommand(storedProcName, connection);
            command.CommandText = storedProcName;
            command.CommandType = CommandType.StoredProcedure;
            foreach (OracleParameter parameter in parameters)
            {
                command.Parameters.Add(parameter);
            }
            connection.Open();

            try
            {
                // start transaction
                command.Transaction = connection.BeginTransaction();
                rowsAffected = command.ExecuteNonQuery();
                command.Transaction.Commit();
            }
            catch (System.Exception ex)
            {
                command.Transaction.Rollback();
                throw ex;
            }

            connection.Close();
            return rowsAffected;
        }
    }
于 2012-05-24T14:23:29.013 に答える