0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C#でDB参照・自動型変換

Last updated at Posted at 2024-12-02

毎度テーブルに合わせて型指定するの大変だな~で作ったもの

※日付型・浮動小数点数には要注意(後述)

テストテーブル

テストなので意地の悪いテーブルを用意

id name float date bool
0 佐藤 0.01 2019/07/02 0:00:00.000 0
1 ヤマダ 0.20 2023/03/03 3:33:33.333 1
2 Mic 1.00 null null

id(int)=int
name(varchar(n))=string
float(float)=float
date(datetime)=DateTime
bool(bit)=bool
として取得参照したい。

接続・取得クラス

変換の弊害

浮動小数点数は2進数で格納➡ほとんどの10進数の小数は2進数で表現➡誤差がでる。
(範囲内であれば正確な格納も可能)
int.Parseなどの変換はnullチェックが必須(解析不可能の為、例外が発生してしまう)
なのでnullを許容するConvertクラスを使用。
DateTimeはただ変換するとミリ秒が削られることを留意。
DateTimeの値がnullの場合として後述のコードでもいいが以下の対策もある。
 1.クエリで抽出時にnullを許さない
 2.DateTime?型(null許容型)に入れる
 3.nullチェック後、任意の値を入れる

SQLRead.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;

public class SQLRead
{
    SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
    public string error_message = null;
    public List<DateTime> dateTimes;
    //各々のDBを参照するなら引数で指定しよう
    public SQLRead()
    {
        builder.DataSource = "あどれす,ぽーと";
        builder.UserID = "ID";
        builder.Password = "PASS";
        builder.InitialCatalog = "データベース名";
        builder.ConnectTimeout = 20;
        //builder.IntegratedSecurity = true;
    }
    //呼び出す・取り出すを考えクエリ文(sql),列(column)を引数にしDataTableで返す
    public DataTable Read(string sql, string[] column)
    {
        //日付確認用
        dateTimes = new List<DateTime>()
        DataTable table = new DataTable();
        //DataTableに列指定(入れ物用意)
        foreach (string i in column) table.Columns.Add(i);
        SqlConnection con = new SqlConnection(builder.ConnectionString);
        try
        {
            con.Open();
            SqlCommand command = new SqlCommand(sql, con);
            SqlDataReader reader = command.ExecuteReader();

            while (reader.Read())
            {
                DataRow dr = table.NewRow();
                foreach (string m in column)
                {
                    System.Type type = reader[m].GetType(reader[m]);
                    //Nullがない場合はtypeで判断できる
                    if (if(type.Name == "DateTime")
                    {
                        //reader.GetOrdinal()でないとミリ秒がとれない
                        DateTime time = reader.GetDateTime(reader.GetOrdinal(m));
                        //文字列として入れる
                        dr[m] = time.ToString("yyyy-MM-dd HH:mm:ss.fff");
                        //文字列として入れる⓶
                        //dr[m] = String.Format("{0:yyyy-MM-dd HH:mm:ss.fff}", time);
                        //DataTableにDateTime型として入れるとミリ秒が削れる
                        //dr[m] = time; ➡2019/07/02 0:00:00
                        //reader.GetOrdinal()で取得しDateTime型にいれるなら問題ない
                        dateTimes.Add(time);
                        
                    }
                    //※●●.Parseはnullでは返れない為Convert
                    else dr[m] = Convert.ChangeType(reader[m], type);
                }
                table.Rows.Add(dr);
            }
        }
        catch (Exception ex) { this.error_message = ex.Message; }
        finally
        {
            con.Close();
            con.Dispose();
        }
        return table;
    }
}

参照クラス

using System;
using System.Collections.Generic;
using System.Data;

private void allRead()
{
    string sql = "SELECT id,name,float,date,bool FROM test";//クエリ
    string[] column = new string[] { "id","name","float","date","bool" }//カラム指定
    SQLRead sql_read = new SQLRead();
    DataTable dt = sql_read.Read(sql,column);
    if(sql_read.error_message == null)
    {
        //dataGridViewをレイアウトに置くとすぐ目視出来て便利
        dataGridView1.DataSource = dt;
        dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
        if (dt.Rows.Count > 0)
        {
            //取り出してみる
            for (int i = 0; i < dt.Rows.Count; i++)
            {
                Console.WriteLine("id:"+ dt.Rows[i].ItemArray[0].ToString());
                Console.WriteLine("name:"+ dt.Rows[i].ItemArray[1].ToString());
                Console.WriteLine("float:"+ dt.Rows[i][2].ToString());
                Console.WriteLine("date:"+ dt.Rows[i]["date"].ToString());
                Console.WriteLine("dateList:"+ dt.Rows[i]["bool"].ToString());
            }
        }
    }
    else Console.WriteLine(read.error_message);
    //List<DateTime>確認
    Console.WriteLine( sql_read.dateTimes.Count());
}

※以下2024-12-03追記

結果

00.PNG
Console.WriteLine( request.dateTimes.Count()) ➡ 2
nullがあってもエラー無く参照できている。
しかしdateとboolの値がnullの場合
Type type = reader[m].GetType()でのtype.NameがDBNullとして処理され値が入っていない。
またif(type.Name == "DateTime")に入らない為、sql_read.dateTimesリストのカウントが2だ。
クエリで整えた方がC#内での自動化としてはスッキリする。

SELECT id,name,float,IsNull(date,'1900-01-01 00:00:00.000') as date,IsNull(bool,0) as bool FROM test

04.PNG

おまけ C#で任意の値を入れる

もはや自動ではない。
type.NameがDBNull...この場合カラムで見る等の工夫が必要。
nullチェック後、任意の値を入れる。苦行。

SQLRead.cs
    while(reader.Read())
    {
        DataRow dr = table.NewRow();
        foreach (string m in column)
        {
            Type type = reader[m].GetType();
            //カラム名で判別する
            if (m == "date")
            {
                if (type.Name == "DBNull") dr[m] = DateTime.Parse("1900-01-01 00:00:00.000").ToString("yyyy-MM-dd HH:mm:ss.fff");
                else
                {
                    DateTime time = reader.GetDateTime(reader.GetOrdinal(m));
                    dr[m] = time.ToString("yyyy-MM-dd HH:mm:ss.fff");
                }
            }
            else if(m == "bool")  dr[m] = type.Name == "DBNull"  || (Boolean)reader[m] == false ? false:true;
            else dr[m] = Convert.ChangeType(reader[m], type);
        }
    }
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?