C#ジェネリック + データベース
データベース関係のソースコードを見ていて,
OleDb・Odbc両方使えるクラスがあってこれは便利だなって感動したのですが,
継承・オーバーライド前提のクラス・・・
個人的に継承には苦手意識ガガガ
オーバーライドも使用しているクラスが違うだけの内容が同じメソッドにしているだけ
クラスが自由に選べるメソッドを作れれば,半分ぐらいになるのにな・・・
そういえば,参考書にジェネリックっていう型をパラメータにできる仕組みがあったような・・・
っていうわけで,そのソースコードを参考にしながら,
勉強がてらにジェネリックを使って,データベース専用のクラスを作って見ました
まず,インターフェースを書いていきます.今後使わないかも知れないけど
using System; using System.Data; using System.Data.Common; using System.Diagnostics; #region データベースインターフェース /// ---------------------------------------------------------------------------- /// <summary> /// データベースインターフェース</summary> /// ---------------------------------------------------------------------------- public interface IDataBase : IDisposable { /// <summary> /// データテーブル取得イベントデリゲート</summary> event Action<DataTable> EventGetDataTable; /// <summary> /// データテーブルを取得</summary> DataTable GetDataTable { get; } /// <summary> /// 接続処理</summary> void Connect(); /// <summary> /// 切断処理</summary> void Disconnect(); /// <summary> /// SQL実行(データ取得)</summary> DataTable Query(string sql); /// <summary> /// SQL実行(データ更新)</summary> int Execute(string sql); } #endregion
先ほど書いたインターフェースを継承したデータベースのクラスを書いていきます
詰めが甘い部分があるかも知れませんが・・・
型パラメータに色んな物(intやらstringやら他のクラス)が入ってきたら困るので,【where】の部分で制約しています.
制約の部分に書かれたクラスを継承するクラス,あとパラメータのないコンストラクタがあるクラスしか入りません
ちなみに,トランザクション機能はありません
using System; using System.Data; using System.Data.Common; using System.Diagnostics; #region データベースクラス /// ---------------------------------------------------------------------------- /// <summary> /// <para>データベースクラス</para> /// <para>様々なデータベースに対応できる(かもしれない)データベースクラス</para> /// <para>DBコネクションは静的のため,インスタンス毎に同じ</para> /// </summary> /// <typeparam name="Connection"> /// DbConnection継承クラス</typeparam> /// <typeparam name="Command"> /// DbCommand継承クラス</typeparam> /// <typeparam name="Adapter"> /// DbDataAdapter継承クラス</typeparam> /// <remarks> /// <para>▼型パラメータの組み合わせ(動作確認済み)▼</para> /// <para>【ODBC・・・OdbcConnection / OdbcCommand / OdbcDataAdapter】</para> /// <para>【OleDB・・・OleDbConnection / OleDbCommand / OleDbDataAdapter】</para> /// </remarks> /// ---------------------------------------------------------------------------- public class DataBase<Connection, Command, Adapter> : IDataBase where Connection : DbConnection, new() where Command : DbCommand, new() where Adapter : DbDataAdapter, new() { #region メンバ変数 /// <summary> /// 取得データテーブル</summary> private DataTable dataTable; /// <summary> /// DBコネクション</summary> protected static Connection dbConection; /// <summary> /// DBコマンド</summary> protected Command dbCommand; /// <summary> /// DBアダプター</summary> protected Adapter dbAdapter; /// <summary> /// データテーブル取得イベントハンドラ(引数:DataTable型)</summary> public event Action<DataTable> EventGetDataTable; #endregion #region プロパティ /// ---------------------------------------------------------------------------- /// <summary> /// <para>接続文字列を設定・取得します</para> /// </summary> /// ---------------------------------------------------------------------------- public static string ConnectionString { get; set; } /// ---------------------------------------------------------------------------- /// <summary> /// <para>接続状態を取得します</para> /// </summary> /// ---------------------------------------------------------------------------- public static bool IsConnected { get; set; } /// ---------------------------------------------------------------------------- /// <summary> /// <para>データテーブルを取得します</para> /// </summary> /// ---------------------------------------------------------------------------- public DataTable GetDataTable { get { try { return this.dataTable.Copy(); } catch { return null; } } } #endregion #region コンストラクタ #region static DataBaseClass() /// ---------------------------------------------------------------------------- /// <summary> /// <para>静的コンストラクタ</para> /// </summary> /// ---------------------------------------------------------------------------- static DataBase() { // インスタンスを生成 dbConection = new Connection(); } #endregion #region DataBaseClass() /// ---------------------------------------------------------------------------- /// <summary> /// <para>オブジェクトを生成します</para> /// </summary> /// ---------------------------------------------------------------------------- public DataBase() { // 接続状態を設定 IsConnected = false; // インスタンス生成 dataTable = new DataTable(); dbCommand = new Command(); dbAdapter = new Adapter(); // DBコマンドで使用するコネクションを登録 dbCommand.Connection = dbConection; // イベント追加 this.EventGetDataTable += new Action<DataTable>(this.OnGetDataTable); } #endregion #region DataBaseClass(string) /// ---------------------------------------------------------------------------- /// <summary> /// <para>接続文字列を指定してオブジェクトを生成します</para> /// </summary> /// <param name="connectionString"> /// 接続文字列</param> /// ---------------------------------------------------------------------------- public DataBase(string connectionString) : this() { // 接続文字列を設定 ConnectionString = connectionString; } #endregion #endregion #region データテーブル取得イベント /// ---------------------------------------------------------------------------- /// <summary> /// <para>データテーブル取得イベント</para> /// </summary> /// <param name="dt"> /// 取得データテーブル</param> /// <remarks> /// <para>データテーブル取得時に発生するイベント</para> /// <para>デバッグ時テーブル取得タイミングを確認します</para> /// <para>オーバーライドにて,処理内容を変更できます</para> /// </remarks> /// ---------------------------------------------------------------------------- protected virtual void OnGetDataTable(DataTable dt) { // デバッグ確認用 DateTime dateTime = DateTime.Now; string dateString = dateTime.ToString("HH:mm:ss.fff"); Debug.WriteLine("【" + dateString + "】 DBデータ取得"); } #endregion #region 接続処理 /// --------------------------------------------------------------------------------------- /// <summary> /// <para>接続処理を行います</para> /// </summary> /// --------------------------------------------------------------------------------------- public void Connect() { // 既に接続された状態なら,処理を中断 if (IsConnected == true) { return; } try { // DBコネクションに接続文字列を設定 dbConection.ConnectionString = ConnectionString; // DBコネクションの状態を取得.開いてるなら処理終了 if (dbConection.State == ConnectionState.Open) { return; } // 接続 dbConection.Open(); // 接続状態をTrueに変更 IsConnected = true; } catch (Exception ex) { throw ex; } } #endregion #region 切断処理 /// --------------------------------------------------------------------------------------- /// <summary> /// <para>切断処理を行います</para> /// </summary> /// --------------------------------------------------------------------------------------- public void Disconnect() { // 接続されてない場合,処理終了 if (IsConnected == false) { return; } try { // 既に切断されている場合は処理を中断 if (dbConection.State == ConnectionState.Closed) { return; } // 切断 dbConection.Close(); // 接続状態を登録 IsConnected = false; } catch (Exception ex) { throw ex; } } #endregion #region SQL実行 #region データ取得 /// --------------------------------------------------------------------------------------- /// <summary> /// <para>データベースよりデータを取得します</para> /// </summary> /// --------------------------------------------------------------------------------------- public DataTable Query(string sql) { // 接続されてない場合,処理終了 if (IsConnected == false) { return null; } try { // SQL文設定 dbCommand.CommandText = sql; dbAdapter.SelectCommand = dbCommand; // 残っているデータをクリア this.dataTable.Clear(); // データテーブルを取得 dbAdapter.Fill(this.dataTable); // イベント発生 this.EventGetDataTable(this.dataTable); // 取得データテーブルを返す return this.dataTable; } catch (Exception ex) { // データをクリア this.dataTable.Clear(); throw ex; } } #endregion #region データベースを更新 /// --------------------------------------------------------------------------------------- /// <summary> /// <para>データベースを更新します</para> /// </summary> /// --------------------------------------------------------------------------------------- public int Execute(string sql) { // 接続されていない場合,処理終了 if (IsConnected == false) { return 0; } try { // SQL文設定 dbCommand.CommandText = sql; // SQL実行 return dbCommand.ExecuteNonQuery(); } catch (Exception ex) { throw ex; } } #endregion #endregion #region リソース開放 #region Dispose() /// --------------------------------------------------------------------------------------- /// <summary> /// <para>リソース開放</para> /// <para>DBアダプターとDBコマンドのリソースを開放します</para> /// <para>DBコネクションはDisposeしません</para> /// <para>DBコネクションもDisposeする場合はDisposeAllメソッドを使用してください</para> /// </summary> /// --------------------------------------------------------------------------------------- public void Dispose() { if (this.dataTable != null) { this.dataTable.Dispose(); } if (this.dbAdapter != null) { this.dbAdapter.Dispose(); } if (this.dbCommand != null) { this.dbCommand.Dispose(); } } #endregion #region DisposeAll() /// ---------------------------------------------------------------------------- /// <summary> /// <para>リソース開放</para> /// <para>DBコネクション・DBアダプタ・DBコマンドすべてのリソースを開放します</para> /// </summary> /// ---------------------------------------------------------------------------- public void DisposeAll() { if (dbConection != null) { this.Disconnect(); dbConection.Dispose(); } if (this.dataTable != null) { this.dataTable.Dispose(); } if (this.dbAdapter != null) { this.dbAdapter.Dispose(); } if (this.dbCommand != null) { this.dbCommand.Dispose(); } } #endregion #endregion } #endregion
このクラスは,DBコネクションをstaticにしています
まぁ,インスタンスごとに違うデータベースにアクセスすることは無いゾっていう前提で作っていますんで・・・
そのためリソース開放にDispose・DisposeAllの2つ作っています
これでいいのかしら・・・
次のデータで実際に使ってみます
・MySQLのtestデータベースのtestテーブル
using System; using System.Data; using System.Data.Odbc; using System.Data.OleDb; namespace Program { // クラス名が長すぎるので変更 using OdbcDb = DataBase<OdbcConnection, OdbcCommand, OdbcDataAdapter>; using OleDb = DataBase<OleDbConnection, OleDbCommand, OleDbDataAdapter>; class Program { static void Main() { try { // ODBC接続文字列(MySQL) string cntStrOdbc = "DSN=MySQL_Server;" + "DATABASE=test;" + "SERVER=192.168.100.101;" + "PORT=3306;" + "UID=ユーザ名;" + "PWD=パスワード;"; // OLEDB接続文字列(Excel) string cntStrOle = "Provider=Microsoft.Jet.OLEDB.4.0;" + "Data Source=./ExcelDB.xls;" + "Extended Properties=Excel 8.0;"; // インスタンス生成 int dbCount = 2; // DBの接続数 OdbcDb dbOdbc = new OdbcDb(); OleDb dbOle = new OleDb(); // 接続文字列を設定 OdbcDb.ConnectionString = cntStrOdbc; OleDb.ConnectionString = cntStrOle; // データベースインターフェース配列 IDataBase[] iDb = new IDataBase[dbCount]; // それぞれのデータベースをインターフェースで参照できるように iDb[0] = dbOdbc; iDb[1] = dbOle; // SQL文生成 string[] sql = new string[dbCount]; sql[0] = "SELECT * FROM test"; sql[1] = "SELECT * FROM [sheet1$A1:C4]"; DataTable dt = new DataTable(); for (int i = 0; i < dbCount; i++) { // インターフェースで接続.データ取得.切断 iDb[i].Connect(); dt = iDb[i].Query(sql[i]); iDb[i].Disconnect(); // データベースの内容を表示 for (int j = 0; j < dt.Columns.Count; j++) { Console.Write(dt.Columns[j].ColumnName + " | "); } Console.WriteLine(""); foreach (DataRow dr in dt.Rows) { for (int j = 0; j < dr.ItemArray.Length; j++) { Console.Write(dr[j].ToString() + " | "); } Console.WriteLine(""); } Console.WriteLine(""); } dbOdbc.DisposeAll(); dbOle.DisposeAll(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } Console.Read(); } } }
クラスが違うだけの,似たような処理はジェネリックを使うと結構楽ですねー