特に、SQLを実行して結果を取得しない「DbCommand.ExecuteNonQuery」や単一の値を求める「DbCommand.ExecuteScalar」であればともかく、SELECTして複数行にわたって処理する場合、「DbCommand.ExecuteReader」を使う必要がありますが、どう頑張ったってこんなコードになっちゃう。
using (var conn = new SqlConnection(connectionString)) using (var cmd = conn.CreateCommand()) { conn.Open(); cmd.CommandText = selectCmd; using (var reader = cmd.ExecuteReader()) { while (reader.Read()) { xxx = reader[columnName]; // いろいろ処理 } } }んむー。せめてwhileじゃなくてforeachを使いたい。できればLINQに繋げたい。そのためには、SELECTの結果のレコードを、IEnumerable<T>で取得できるようなメソッドを用意しておきたい。そこで、DbCommandクラスにこんな拡張メソッドを用意してみます。なお、わかりやすくするためにパラメータのNULLチェックはなしで。
public static IEnumerable<IDataRecord> ExecuteQuery(this DbCommand command) { return ExecuteQuery(command, dr => dr); } public static IEnumerable<T> ExecuteQuery<T>( this DbCommand command, Func<IDataRecord, T> mapper) { using (var reader = command.ExecuteReader()) { while (reader.Read()) { yield return mapper(reader); } } }「ExecuteQuery」という名前の2つの拡張メソッドを作ります。一つはIEnumerable<IDataRecord>を返し、もう一方は任意の変換関数を用意して、IEnumerbale<T>を返します。ちなみに「IDataRecord」はDbCommand.ExecuteReaderメソッドが返す、DbDataReader抽象クラスの派生クラスで実装されます。
これを使うと多少ましになります。例としてなんか適当なバッチ処理を考えてみましょうか。
SQLServerのあるテーブルをCSVファイルにエクスポートします。その際に「exported」カラムが「0」のレコードのみを対象とします。また、エクスポート実行後、対象レコードの「exported」カラムを「1」に、「exportAt」カラムに現在日時をセットします。
こんなありがちな処理を先のDbCommand拡張メソッドを使って書いてみると、こんなコードになります。
const string connectionString = @"Data Source=.\SQLEXPRESS;Initial Catalog=Test;Integrated Security=True"; const string selectCmd = "select id, uid, insertAt, qty from TestTable_1 where exported=0"; const string updateCmd = "update TestTable_1 set exported=1, exportAt=getdate() where id in ({0})"; static void Main(string[] args) { using (var conn = new SqlConnection(connectionString)) { var ids = new List<int>(); using (var cmd = conn.CreateCommand()) using (var csv = new StreamWriter("output.csv")) { conn.Open(); cmd.CommandText = selectCmd; foreach (var r in cmd.ExecuteQuery()) { ids.Add(r.GetInt32(0)); csv.WriteLine(string.Format("{0},{1},{2}", r[1], r[2], r[3])); } } if (ids.Count > 0) { using (var cmd = conn.CreateCommand()) { cmd.CommandText = string.Format(updateCmd, string.Join(",", ids)); cmd.ExecuteNonQuery(); } } } }意外と見やすいコードが書けるんじゃないかと思ってるんですが、どうでしょ?
0 件のコメント:
コメントを投稿