2011年10月17日月曜日

.NET で MySQL を使う ( MySQLDataReader )

C#なんかでMySQLに接続するのは、検索すれば色々と出てくるので割愛するが、
要するに、MySQL本家のサイトから Connector/Net という ODBC をダウンロード&インストールで接続できるようになる。

んで、実際に接続して表示するプログラムのソースも結構な数が出てくるんだが、
MySQLDataReader クラスの記述がどれも似たようなものばかり??のような気がする。
ホントに気のせいかもしれないので、その時は毎度のごとくご容赦を。

ソースのコネクション部分は割愛するが、ポイントだけ。
MySQL は文字化けの問題が昔から言われているが、その殆どはプログラマなどのユーザの問題だ。例えば、以下の MySQL コマンドをどれほどの人が知っているだろうか。

mysql> status

--------------
mysql   Ver 14.14 Distrib 5.1.51, for Win32 (ia32)
・・・
というバージョンに続いて、様々な情報が表示され、最後の方に、
・・・
Servercharacterset:  sjis
Dbcharacterset:  sjis
Clientcharacterset:  sjis
Conn.characterset:  sjis

という、MySQL サーバー、カレントデータベース、クライアント、コネクションと、実に4つの文字コードが設定されていることがわかる。

つまり、C# などのような現在の .net は UNICODE なので、接続する際に接続文字列に、
charset=utf8;
を加えれば、コネクション単位で、アプリケーション(クライアント)の文字コードに合わせることができる。
まぁ、とは言うものの MySQL サーバー・クライアントともにデフォルトが utf8 で、アプリケーションも utf8 なのに文字化けするという難儀な事態も経験したことがあるが、あのときは元々 sjis で途中から変更して...みたいな感じだったと思うが、結論を言うと、コネクション時に sjis を指定すると何故か直った。
あと、ujis を Windows で使うには気をつけないとハマるよ。

今は、UNICODE 全盛時代に突入したそうなので、昔の膨大な sjis 資産を「えいっ!」と変換したほうが良さそう...あぁ昔の sjis しか扱えなかった(もちろんデフォルトでという意味) Builder が懐かしい。

というわけで、本題に。

// C# ソース(一部)

MySQLDataReader reader = null;
MySQLCommand cmd = new MySQLCommand("desc table_name;", mysqlCon);

try
{
    reader = cmd.ExecuteReader();

    while (reader.Read())
    {
        // この部分
        Console.WriteLine(reader.GetString(0) + ", " + reader.GetString(2));
    }
}
catch(MySqlException ex)
{
    Console.WriteLine(ex.Message);
}
finally
{
    if (reader != null) reader.Close();
}


よくもまぁ、あるかどうかもわからないインデックス(2)を指定してくれたもんだ。でもこういうのって一杯ありますよね。
で、素人に毛の生えた程度のプログラマなら、

    while (reader.Read())
    {
        // この部分を以下に変更
        for (int i = 0; i < reader.FieldCount; i++)
        {
            if (i > 0) Console.Write("\t");
            Console.Write(reader.GetString(i));
        }
        Console.Write("\n");
    }

これで完璧だ!と思うのは、まだまだ毛がフサフサなヒヨコで、
(実際、殆どのテーブル操作はこれでOKだったりするから始末が悪い)
MySQL フィールド値として独特の null 値があることを忘れてはならない。

    while (reader.Read())
    {
        // この部分をさらに以下に変更
        for (int i = 0; i < reader.FieldCount; i++)
        {
            if (i > 0) Console.Write("\t");
            if (reader.IsDBNull(i)) Console.Write("null");
            else Console.Write(reader.GetString(i));
        }
        Console.Write("\n");
    }

というふうに、毛の抜けたSEならする。
ここを以下のようにすると、実行時にエラーとなる。
一回味わってみるのも良いだろう...(desc コマンドだし...)

    while (reader.Read())
    {
        // これは悪い例
        for (int i = 0; i < reader.FieldCount; i++)
        {
            if (i > 0) Console.Write("\t");
            if (reader.GetString(i) == null) Console.Write("null");
            else Console.Write(reader.GetString(i));
        }
        Console.Write("\n");
    }

GetString() は、フィールド値を string 型として返すが、null の場合に慣れ親しんだ null は返ってこず、アクセス違反を叩き付けられるので注意が必要。

0 件のコメント:

コメントを投稿