データベースにアクセスするクラスを作ってみた(その2)

データベースにアクセスするクラスを変更しました。
主な変更点としては

  1. テーブル参照の結果をレコードのリストとして取得(レコードのリストの中には項目のデータのリストが入っています。)、
  2. SELECT COUNTなどの集計値を取得するメソッドを追加
  3. テーブルにアクセスするときの共通メソッドの作成

※3について
C#ではメソッドへの参照を保持することができます。(デリゲートといいます。) 具体的には、メソッド内にメソッドを定義でき、任意の場所で実行することができます。今回はこれを利用しました。


以下プログラムです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlServerCe;

namespace TableAccess
{
    class DBAccess
    {
        string connection_str;

        ////////コンストラクタ////////
        public DBAccess(string dsrc, string pwrd){
            // データベース接続用文字列の作成
            connection_str = "Data Source = " + dsrc + "; Password ='" + pwrd + "'";
        }


        ////////集計値確認(SELECT COUNT文など)////////
        public int CountTable(string str)
        {
            int cnt = new Int32();

            /*** テーブルアクセス呼び出し、戻り値を返す **/
            bool result = AccessTable(str, (c) =>
            {
                try
                {
                    // 集計値確認
                    cnt = (int)c.ExecuteScalar();
                    return true;
                }
                catch (Exception)
                {
                    return false;
                }
            });

            if (result == false)
                return -1;
            
            return cnt;
        }

        ////////テーブル更新(UPDATE、INSERT、DELETE文など)////////
        public bool RenewalTable(string str)
        {
            /*** テーブルアクセス呼び出し、戻り値を返す **/
            return AccessTable(str, (c) =>
            {
                try
                {
                    // テーブル更新
                    c.ExecuteNonQuery();
                    return true;
                }
                catch (Exception)
                {
                    return false;
                }
                
            });

        }

        ////////テーブル参照(SELECT文など)////////
        public List<object> ReferTable(string str)
        {
            List<object> records = null;
            try
            {
                /*** 複数レコードリストを生成 ***/
                records = new List<object>();

                /*** テーブルアクセス呼び出し***/
                bool result = AccessTable(str, (c) =>
                {
                    // データリストを生成
                    List<string> item_values = new List<string>();

                    // データ読み込みストリーム生成
                    SqlCeDataReader scdr = c.ExecuteReader();

                    // 読み込むレコードが無くなるまで、次のレコードへ移動
                    while (scdr.Read())
                    {
                        Func<List<string>> func = ()=>
                        {
                            List<string> v = new List<string>();
                            for (int i = 0; i < scdr.FieldCount -1; i++)
                                v.Add(scdr.GetString(i));
                            return v;
                        };

                        records.Add(func());
                    }

                    // データ読み込みストリームを閉じる
                    scdr.Close();

                    return true;
                });

                if (result == false)
                    throw new ApplicationException();

                /*** レコードを返す***/
                return records;
            }
            catch (Exception)
            {
                return null;
            }            
        }

        ////////テーブルアクセス////////
        private bool AccessTable(string str,Func<SqlCeCommand, bool> func)
        {

            SqlCeConnection conn = null;
            SqlCeCommand cmd = null;
            try
            {
                /*** データベースに接続 ***/
                conn = new SqlCeConnection(connection_str);
                conn.Open();

                /*** SQL文実行 ***/
                cmd = conn.CreateCommand();
                cmd.CommandText = str;
                bool r = func(cmd);

                if (r == false)
                    throw new ApplicationException();

                return true;
            }
            catch (Exception)
            {
                return false;
            }
            finally
            {
                if ( cmd != null)
                    cmd.Dispose();
                if (conn != null)
                    conn.Close();
            }
        }        
    }


    // ここから下はDBAccessオブジェクトを生成して、実際にメソッドを実行
    class Program
    {
        static void Main(string[] args)
        {
           
            // DBAccessオブジェクト生成
            DBAccess dba = new DBAccess("dbfile3.sdf", "password123");

            // テーブル更新
            Console.WriteLine("テーブル更新します。");
            bool result1;
            result1 = dba.RenewalTable("INSERT INTO 電話帳テーブル ([名前], [メールアドレス],[電話番号]) Values('うううう', 'bbbb.jp', '2222-2222')");
            if (result1 == false)
            {
                Console.WriteLine("テーブル更新に失敗しました。");
                Console.ReadLine();
                return;
            }
            Console.WriteLine("\n\n\n");

            // 集計値確認
            Console.WriteLine("集計値確認を行います。");
            int result2;
            result2 = dba.CountTable("SELECT COUNT(*) FROM 電話帳テーブル");
            if (result2 < 0)
            {
                Console.WriteLine("集計値確認に失敗しました。");
                Console.ReadLine();
                return;
            }
            Console.WriteLine(result2);
            
            Console.WriteLine("\n\n\n");

            // テーブル参照
            Console.WriteLine("テーブル参照します。");
            List<object> records = dba.ReferTable("SELECT * FROM 電話帳テーブ");
            if (records == null)
            {
                Console.WriteLine("テーブル参照に失敗しました。");
                Console.ReadLine();
                return;
            }
            string out_line;
            foreach (List<string> r in records)
            {
                out_line = "";
                foreach (string data in r)
                    out_line += data + ",";

                Console.WriteLine("{0}\n", out_line);
            }
            Console.WriteLine("\n\n\n");
            Console.ReadLine();
            records.Clear();
            return;
        }
    }
}

実際にテーブルにアクセスするメソッドはAccessTableです。AccessTableの引数は以下のようにしました。

AccessTable(string str,Func<SqlCeCommand, bool> func)

第一引数(str)はSQL文,第二引数(func)がデリゲートです。型がFuncとなっていますが、SqlCeCommand型を引数として、bool型を返すデリゲートを意味します。メソッド内の以下のように呼び出されています。

bool r = func(cmd);

では、このfuncはどこで定義されているかというと、CountTableメソッド、RenewalTableメソッド、ReferTableメソッドのAccessTable呼び出し時です。例えば、CountTableメソッドでは以下のように呼び出しています。

return AccessTable(str, (c) =>
{
    try
    {
        // テーブル更新
        c.ExecuteNonQuery();
        return true;
    }
    catch (Exception)
    {
        return false;
    }
    
});

(c) => {...};という部分がそうです。(改行が入っていて分かりにくいですが) これはラムダ式といって、デリゲートを定義できます。cは引数になります。(任意の文字でcでなくてもかまいません。)


また、ReferTableメソッドの以下個所においても、デリゲートを使用しています。

// 読み込むレコードが無くなるまで、次のレコードへ移動
while (scdr.Read())
{
    Func<List<string>> func = ()=>
    {
        List<string> v = new List<string>();
        for (int i = 0; i < scdr.FieldCount -1; i++)
            v.Add(scdr.GetString(i));
        return v;
    };

    records.Add(func());
}

1レコード内の個々のstringのデータをListに追加し、そのListを取得レコード全体としてのList(records)に追加するという処理を「records.Add(func());」という個所で行っています。func()の定義は「Func> func = ()=> {...}」のラムダ文で行っています。