質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.35%

OraclePROCEDUREのOUTパラメータでTYPEを指定したとき、C#で受け渡しを行いたい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,540

score 0

前提・実現したいこと

Oracle11gでPROCEDUREを作成しました。
OUTパラメータとして、TYPEをしていしております。
C#プログラムからこのPROCEDUREを呼び出したときにOUTパラメータを受け取り、
結果表示したいと考えております。

発生している問題・エラーメッセージ

PROCEDUREを呼び出した際にOUTパラメータの受け取り時にエラーが発生します。

「'TB_P_TEST'の呼出しで、引数の数または型が正しくありません。」

OUTパラメータを受け取り方がわかっておりません。

該当のソースコード

【Oracle側】
<TYPE>

CREATE OR REPLACE TYPE TB_T_KAIKOBI FORCE
AS OBJECT (
    sKaikobi             VARCHAR2(8),
    iTeiinCheck          NUMBER(1,0)
)
/
CREATE OR REPLACE TYPE TB_T_KAIKOBI_A FORCE
AS TABLE OF TB_T_KAIKOBI
/

<PROCEDURE>

CREATE OR REPLACE PROCEDURE TB_P_TEST
( p_in_iBumon         IN   NUMBER   
  , p_out_aKaikobi  OUT  TB_T_KAIKOBI_A
  )
IS
  infoKaikobi TB_T_KAIKOBI;

BEGIN
  /* 返り値セット */ 
  p_out_aKaikobi := TB_T_KAIKOBI_A();
  p_out_aKaikobi.extend(2);   --2コの配列 

  --開講日情報の1つ目の要素(1-1-1)

  infoKaikobi := TB_T_KAIKOBI('20201112',0);
  p_out_aKaikobi(1) := infoKaikobi;

  --開講日情報の2つ目の要素(1-1-2)

  infoKaikobi := TB_T_KAIKOBI('20201113',1);
  p_out_aKaikobi(2) := infoKaikobi;

  COMMIT;

END;
/

※INパラメータの「p_in_iBumon」は実際には使用しますが、
テストPROCEDUREでは使用しなくしております。

【C#側】

    private void btnTest_Click(object sender, EventArgs e)
    {
      List<object> listValue = new List<object>();
      listValue.Add(1);

      List<OleDbParameter> listInitOut = GetProcedure(listValue, "TB_P_TEST");
    }


    /// <summary>
    /// プロシージャ情報の取得
    /// </summary>
    public List<OleDbParameter> GetProcedure(List<object> listValue, string strProcedure)
    {
      //データベースの接続先を指定
      using (OleDbConnection OraConn = new OleDbConnection(/*接続先文字列*/))
      {
        using (OleDbCommand Cmd = new OleDbCommand())
        {
          //データベース接続を開く
          OraConn.Open();
          Cmd.Connection = OraConn;
          //コマンドタイプをストアドプロシージャにする
          Cmd.CommandType = CommandType.StoredProcedure;
          //実行するストアドプロシージャを指定
          Cmd.CommandText = strProcedure;

          //ストアドプロシージャのin用パラメータ作成
          List<string> listInName = new List<string>();
          List<OleDbType> listInType = new List<OleDbType>();

          listInName.Add("1");
          listInType.Add(OleDbType.Decimal)

          //Inパラメータの追加
          int intInPrameter = 0;
          foreach (string strName in listInName)
          {
            Cmd.Parameters.Add(strName, listInType[intInPrameter]).Value = listValue[intInPrameter];
            intInPrameter++;
          }

          //ストアドプロシージャのout用パラメータ作成
          List<OleDbParameter> listOutpara = new List<OleDbParameter>();

          //リストのタイプを作成
          List<string> listOutName = new List<string>();
          List<OleDbType> listOutType = new List<OleDbType>();
          OutParamInfo outInfo = new OutParamInfo();

          listOutName.Add("開講日情報");        //←ここのセット方法がどのようにすればよいかがわかっておりません
          listOutType.Add(OleDbType.Binary);    //←ここのセット方法がどのようにすればよいかがわかっておりません

          //Outパラメータの追加
          int intOutPrameter = 0;

          foreach (string strName in listOutName)
          {
            OleDbParameter outPara = new OleDbParameter(strName, listOutType[intOutPrameter]);
            outPara.Direction = ParameterDirection.Output;
            outPara.Size = 40;
            listOutpara.Add(outPara);
            Cmd.Parameters.Add(outPara);
            intOutPrameter++;
          }

          //ストアドプロシージャの実行
          Cmd.ExecuteNonQuery();

          return listOutpara;
        }
      }
    }

上記からODP.NETを使用したソースに変更しました。
TYPEを使用しないPROCEDUREの場合、成功しました。

以下は配列をやめてみて、TYPEのみの受け渡しを記載しました。

実行すると「Cmd.ExecuteNonQuery(); 」の部分でエラーが発生します。
TYPEでない場合、成功するので、listOutType.Add(OracleDbType.Object); が
悪いのか、「listOutName.Add("p_out_Kaikobi"); 」の設定が悪いと思っております。

バインディングというのがピンときておりません。

CREATE OR REPLACE PROCEDURE TB_P_TEST2
( p_in_iBumon         IN   NUMBER   
  , p_out_Kaikobi  OUT  TB_T_KAIKOBI
  )
IS

BEGIN
  /* 返り値セット */ 
  p_out_Kaikobi := TB_T_KAIKOBI('20201112',0);

  COMMIT;

END;
/
    private void btnTest_Click(object sender, EventArgs e)
    {
      List<object> listValue = new List<object>();
      listValue.Add(1);

      List<OracleParameter> listInitOut = GetProcedure(listValue, "TB_P_TEST2");
    }


    /// <summary>
    /// プロシージャ情報の取得
    /// </summary>
    public List<OracleParameter> GetProcedure(List<object> listValue, string strProcedure)
    {
      //データベースの接続先を指定
      OracleConnection OraConn = new OracleConnection();
      OraConn.ConnectionString = /*接続先文字列*/;
      OraConn.Open();


      OracleCommand Cmd = new OracleCommand();
          //データベース接続を開く
          OraConn.Open();
          Cmd.Connection = OraConn;
          //コマンドタイプをストアドプロシージャにする
          Cmd.CommandType = CommandType.StoredProcedure;
          //実行するストアドプロシージャを指定
          Cmd.CommandText = strProcedure;

          //ストアドプロシージャのin用パラメータ作成
          List<string> listInName = new List<string>();
          List<OracleDbType> listInType = new List<OracleDbType>();

          listInName.Add("1");
          listInType.Add(OracleDbType.Decimal)

          //Inパラメータの追加
          int intInPrameter = 0;
          foreach (string strName in listInName)
          {
            Cmd.Parameters.Add(strName, listInType[intInPrameter]).Value = listValue[intInPrameter];
            intInPrameter++;
          }

          //ストアドプロシージャのout用パラメータ作成
          List<OracleParameter> listOutpara = new List<OracleParameter>();

          //リストのタイプを作成
          List<string> listOutName = new List<string>();
          List<OracleDbType> listOutType = new List<OracleDbType>();
          OutParamInfo outInfo = new OutParamInfo();

          listOutName.Add("p_out_Kaikobi");        //←ここのセット方法がどのようにすればよいかがわかっておりません
          listOutType.Add(OracleDbType.Object);    //←Objectに変更したところ、型が正しくありませんは解消されました

          //Outパラメータの追加
          int intOutPrameter = 0;

          foreach (string strName in listOutName)
          {
            OracleParameter outPara = new OracleParameter(strName, listOutType[intOutPrameter]);
            outPara.Direction = ParameterDirection.Output;
            outPara.Size = 40;
            listOutpara.Add(outPara);
            Cmd.Parameters.Add(outPara);
            intOutPrameter++;
          }

          //ストアドプロシージャの実行
          Cmd.ExecuteNonQuery();                //←ここで「p_out_Kaikobi」バインディングが無効ですとなります。

          return listOutpara;
        }
      }
    }

試したこと

「OleDbType」を「Binary」としておりますが、他の値でも一通り試してみました。
ネット調べてもC#で%TYPEの取得方法が見つからず、苦慮しております。

補足情報(FW/ツールのバージョンなど)

開発環境は以下の通り
C#
Oracle11g
.Net3.5

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • radian

    2020/11/13 16:36 編集

    接続文字列は変わるかもしれませんが、データ取得や実行の構文は基本的に他のデータプロバイダーと変わらないので、それほど迷うところは無いかと思います。ODP.NET以外の接続方法だとOracle固有の機能やデータ型が使えなかったりするらしいので、物は試しというやつですね。
    いきなり本番のソースに組み込まずに、別プロジェクトで小さいサンプルを作って試してみた方がよいかと思います。

    キャンセル

  • mShintani

    2020/11/13 16:48

    ありがとうございます。
    >ODP.NET以外の接続方法だとOracle固有の機能やデータ型が使えなかったりするらしいので
     ⇒そうなんですね。
      大変参考になります。
    一度、サンプルで実装してみます。

    キャンセル

  • mShintani

    2020/11/13 17:37

    質問内容をODP.NETに変更した分を追記し、PROCEDUREも配列ではなく、まずはTYPEの受け渡しのみにしてみました。
    前回のエラーは解消したのですが、別のエラーとなってしまいました。
    自分でも調べてはいるのですが、納期の都合上、こちらに質問として載せさせていただきます。

    キャンセル

回答 2

check解決した方法

0

今回は解決にはならなかったのですが、PROCEDUREの更新方法を大きく見直して、問題は解決しました。
実施したこととしては、OUTでもらうはずのものをPROCEDURE内でテーブルに更新して、更新後に結果(0:OK、1:NG)を返す。その後、OKが返ってきてから、更新したテーブルをSELECTするという方法にしました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

ユーザー定義型をInputパラメータで渡しているサンプルコードがありました、がOutputパラメータでも使用出来るかどうかは定かではありません。
ODP.NET/C# を使ってストアどプロシージャに一時BLOBデータを渡し方、およびユーザー定義型での受け渡し方のサンプル

ODP.NETには管理対象ドライバと管理対象外ドライバがありますが、管理対象ドライバではObject型は使用できないかもしれません。
OracleDbTypeの列挙

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/11/16 08:54 編集

    11g, NET 3.5 ということなので、ODP.NET 2.0 ですね。
    「Oracleユーザー定義タイプ(UDT)および.NETカスタム・タイプ」
    https://docs.oracle.com/cd/E16635_01/win.111/e06104/featUDTs.htm
    ただ、管理対象ドライバ(Core用含む)では使えないため、今後を考えると表関数を使ったほうが使い勝手がいいかもしれません。
    https://www.shift-the-oracle.com/plsql/table-function/

    キャンセル

  • 2020/11/16 18:44

    radianさん、KOZ6.0さん。ご回答ありがとうございます。
    お二方のやり方を参考にしたのですが、OUTパラメータをUDTでもらうのはできませんでした。
    今回は解決にはならなかったのですが、PROCEDUREの更新方法を大きく見直して、問題は解決しました。
    実施したこととしては、OUTでもらうはずのものをPROCEDURE内でテーブルに更新して、更新後に結果(0:OK、1:NG)を返す。その後、OKが返ってきてから、更新したテーブルをSELECTするという方法にしました。

    キャンセル

  • 2020/11/17 10:21

    なるほど、違う方法で解決したのですね。
    解決したのであれば、それを自分で回答に記入してベストアンサーを付けて終了させてください。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.35%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る