none
SqlDbTypeからの.NET Framework 型の取得 RRS feed

  • 質問

  • お世話になります。


    ADO.NETでパラメーター(SqlParameter)を作成時にSqlDbTypeと値(Value)を
    指定しますが、このパラメーターから値を取得しようと考えます。

    下記参考ページのこの部分→Int32 categoryID = (Int32) command.Parameters["@Identity"].Value;

     

    参考ページhttp://msdn.microsoft.com/ja-jp/library/3btz0xwf.aspx

    // Assumes connection is a valid SqlConnection.
    SqlCommand command = new SqlCommand("InsertCategory" , connection);
    command.CommandType = CommandType.StoredProcedure;

    SqlParameter parameter = command.Parameters.Add(
      "@RowCount", SqlDbType.Int);
    parameter.Direction = ParameterDirection.ReturnValue;

    parameter = command.Parameters.Add(
      "@CategoryName", SqlDbType.NChar, 15);

    parameter = command.Parameters.Add("@Identity", SqlDbType.Int);
    parameter.Direction = ParameterDirection.Output;

    command.Parameters["@CategoryName"].Value = "New Category";
    command.ExecuteNonQuery();

    Int32 categoryID = (Int32) command.Parameters["@Identity"].Value;
    Int32 rowCount = (Int32) command.Parameters["@RowCount"].Value;

     


    その際、型を動的に取得する方法はありますでしょうか?
    この方法は(Int32)と記述しておりますが汎用的に、どうにか出来ないか?と考えます。

    下記ページに対比表がありますが
    http://msdn.microsoft.com/ja-jp/library/yy6y35y8.aspx
    SqlDbTypeがNVarCharなら対比する.NET Framework 型のsystem.stringを取得したいのです。

    良い方法などありましたら、ご教示よろしくお願いいたします。

    2008年11月1日 15:11

回答

  • ご回答ありがとうございます。

     

    Azuleanさん。
    サンプルソースありがとうございます。
    非常に参考になりました。

     

    SurferOnWwwさん
    「その手間をかけるだけのメリット」ですが、
    Parametersをセットする人と、使用する人(例えばExceptionをログに吐き出し、パラメーター値も出力)が違う場合
    型を意識した記述をさせない方法として「SqlDbTypeからの.NET Framework 型の取得」が出来れば
    良いのでは?と考えたのが質問の始まりでした。

     

    もう少し検討してみます。ありがとうございました。

     

    2008年11月3日 5:37

すべての返信

  •  AriAriビスカス さんからの引用

    Int32 categoryID = (Int32) command.Parameters["@Identity"].Value;
    Int32 rowCount = (Int32) command.Parameters["@RowCount"].Value;


    その際、型を動的に取得する方法はありますでしょうか?
    この方法は(Int32)と記述しておりますが汎用的に、どうにか出来ないか?と考えます。

    下記ページに対比表がありますが
    http://msdn.microsoft.com/ja-jp/library/yy6y35y8.aspx
    SqlDbTypeがNVarCharなら対比する.NET Framework 型のsystem.stringを取得したいのです。

    良い方法などありましたら、ご教示よろしくお願いいたします。

     

    何か矛盾しているような気がしますが・・・ 

     

    何故なら、右辺と左辺の型を一致させるわけですが、プログラマが左辺(これは動的に

    変わらない)を Int32 rowCount と書くということは、右辺には (Int32) と書くしかあり得

    ないわけで、汎用的にどうにかできる必要はないと思いますけど。

     

    ただし、左辺が String 型で、右辺が色々変わる場合は、以下のようにするということも

    あると思います。

     

    Code Snippet

    protected string GetStringFromReader(SqlDataReader reader)
        {
            string text = String.Empty;
            for (int i = 0; i < reader.FieldCount; i++)
            {
                object obj = reader[i];
                if (obj is DBNull)
                {
                    text += "DBNull, ";
                }
                else if (obj == null)
                {
                    text += "null, ";
                }
                else if (obj is Int32)
                {
                    text += "Int32: " + ((Int32)obj).ToString() + ", ";
                }
                else if (obj is String)
                {
                    text += "String: " + (string)obj + ", ";
                }
                else if (obj is DateTime)
                {
                    text += "DateTime: " + ((DateTime)obj).ToShortDateString() + ", ";
                }
                else if (obj is Decimal)
                {
                    text += "Decimal: " + ((decimal)obj).ToString() + ", ";
                }
                else
                {
                    text += "Unknown, ";
                }
            }
            text.TrimEnd(new char[] { ',', ' ' });
            text += "<br />";
            return text;

     

     

     

    2008年11月2日 5:08
  •  AriAriビスカス さんからの引用

    その際、型を動的に取得する方法はありますでしょうか?
    この方法は(Int32)と記述しておりますが汎用的に、どうにか出来ないか?と考えます。

    どのようにしたいのかがよく見えません。

    実際に書くとコンパイルエラーになるコードでも良いので、イメージを示して頂けませんか?

     

    現時点の情報だけだと、私も矛盾を感じます。

    単純にキャストを書きたくないと言っても、やはりどこかでその値を特定の型とみなして処理するはずなので、動的に取得したい理由が見えません。

     

    Code Snippet

        object o = null; // 本来は何かの値。例えばValueプロパティの戻り値など。

        string s = GetValue<string>(o); // この位置で何型で欲しいか静的に決まっている。


     

    static T GetValue<T>(object o)
    {
        if (o == null) return default(T);
        if (o is T) return (T)o;
        throw new InvalidCastException();
    }

     

     

    単純にType型が欲しいのであれば、GetTypeメソッドでできますけれど。

    同じ理屈でToStringメソッドもobject型で実装されているので、何らかの値か型の名前を得ることができます。

    2008年11月2日 9:38
    モデレータ
  • SurferOnWwwさん
    Azuleanさん

    回答ありがとうございます。


    漠然としたイメージになってしまいますが、
    command.Parameters["@Identity"].Valueを一旦、hashtableなどに保管(add)しておいて
    必要になった時に取り出せないか?と言うのが最終的にやりたいことです。

     

    <イメージ>

    private struct sqlBindStruct
    {
        public SqlDbType serverType;
        public object dbValues;
    }

     

    //登録
    sqlBindStruct bs1 = new sqlBindStruct();
    sqlBindStruct bs2 = new sqlBindStruct();

    bs1.serverType = SqlDbType.Int;
    bs1.dbValues = command.Parameters["@Identity"].Value;

    bs2.serverType = SqlDbType.NChar;
    bs2.dbValues = command.Parameters["@CategoryName"].Value;

     

    Hashtable ht = new Hashtable();
    ht.Add("Identity",bs1);
    ht.Add("CategoryName",bs2);

     

    //取り出し
    ??? bindData= (???)((sqlBindStruct)(ht["Identity"])).dbValues;


    1.構造体sqlBindStructを用意しておき、"SqlDbType"と"値"をセット
    2.これをhashtableへ登録
    3.取り出し時に適正なキャストし取り出す

    この取り出しの際SqlDbTypeより、.NET Framework 型を取得しキャスト(???部)
    上記の場合はstring型の@CategoryNameとint型の@Identityが混在してaddされているイメージです。


    指摘いただいた左辺の型(これは動的に変わらない)も、合わせてうまく
    記述できれば?と考えますが、やっぱり無理ですかね・・・

    >単純にType型が欲しいのであれば、GetTypeメソッドでできますけれど。
    Azuleanさん。このことでしょうか?command.Parameters["@Identity"].Value.GetType()

    2008年11月2日 13:50
  •  AriAriビスカス さんからの引用

    指摘いただいた左辺の型(これは動的に変わらない)も、合わせてうまく
    記述できれば?と考えますが、やっぱり無理ですかね・・・

    この左辺の型に代入する部分はいずれにせよ、キャストが必要です。

     

    .NET Framework 2.0以降であればHashTableクラスの代わりにDictionaryクラスを使えば、キャストの回数を1回減らせますが、基本的に AAA aaa = (AAA)dictionary["Identity"].dbValue; といったように、型を2回書く必要があるのは変わりません。

     

    キャストを書く回数を減らすだけなら、下記のような書き方もできます。

     

    Code Snippet

    private struct TestStore

    {

        public object data;

    }

     

    private void Test()
    {
        TestStore test = new TestStore();
        test.data = 12;
        int aaa;
        GetValue(test, out aaa); // aaa = 12

        string bbb;
        GetValue(test, out bbb); // bbb = null
    }

     

    private void GetValue<T>(TestStore source, out T output)
    {
        if (source.data != null && source.data is T)
        {
            output = (T)source.data;
        }
        else
        {
            output = default(T);
        }
    }

     

     

    ※戻り値の型が違うだけの場合、型の推論を行ってくれないため、GetValue<int>みたいな書き方が必要になります。それを避けるためにoutキーワードで引数にしています。

     

     AriAriビスカス さんからの引用

    >単純にType型が欲しいのであれば、GetTypeメソッドでできますけれど。
    Azuleanさん。このことでしょうか?command.Parameters["@Identity"].Value.GetType()

    そうですが、今回の用途には使えません。

    2008年11月2日 16:15
    モデレータ
  •  AriAriビスカス さんからの引用

    1.構造体sqlBindStructを用意しておき、"SqlDbType"と"値"をセット
    2.これをhashtableへ登録
    3.取り出し時に適正なキャストし取り出す

    この取り出しの際SqlDbTypeより、.NET Framework 型を取得しキャスト(???部)
    上記の場合はstring型の@CategoryNameとint型の@Identityが混在してaddされているイメージです。

     

    個人的な意見としては、手間が多いのに、その手間をかけるだけのメリットがほと

    んど無いように思えます。

     

    イメージをお持ちなら、一度実際にサンプルを作るなどして、検討してみてはいか

    がでしょう?

     

    そのイメージと普通(?)にキャストすることのメリット/デメリットを比較して、メリット

    があるか否かご自分で判断してみてください。

     

    2008年11月2日 17:02
  • ご回答ありがとうございます。

     

    Azuleanさん。
    サンプルソースありがとうございます。
    非常に参考になりました。

     

    SurferOnWwwさん
    「その手間をかけるだけのメリット」ですが、
    Parametersをセットする人と、使用する人(例えばExceptionをログに吐き出し、パラメーター値も出力)が違う場合
    型を意識した記述をさせない方法として「SqlDbTypeからの.NET Framework 型の取得」が出来れば
    良いのでは?と考えたのが質問の始まりでした。

     

    もう少し検討してみます。ありがとうございました。

     

    2008年11月3日 5:37