毎度テーブルに合わせて型指定するの大変だな~で作ったもの
テストテーブル
テストなので意地の悪いテーブルを用意
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チェック後、任意の値を入れる
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追記
結果
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
おまけ C#で任意の値を入れる
もはや自動ではない。
type.NameがDBNull...この場合カラムで見る等の工夫が必要。
nullチェック後、任意の値を入れる。苦行。
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);
}
}