プログラムの概要
ACCESS 等のデータベースの操作に不慣れな者でも、簡単 に使用する事が出来るようなデータベースアプリケーショ ンを目指します。 今回は、データグリッドをクリックする事に依り、其のレ コードのフィールドデータを個別にテキストボックスやコ ンボボックスに表示する機能を付加する。此の際、カテゴ リIDや著者IDの様に人間に解り難いデータ形式では無 く、名称で表示する事に依り、操作性を高めて居る。 亦、テキストボックスやコンボボックスで入力したデータ を追加したり、表示された内容に変更を加えて更新したり 表示されたレコードを削除したりする機能を付加する。 VB でデータベースを操作する為の基本と成る機能を盛り 込んで有るので、色々と工夫し、発展させて、操作性の良 いアプリケーションに仕上げて下さい。データベース操作プログラムⅢ
C# 2005 ⑳□ コレクションの利用(ComboBox.Items、ADODB.Recordset.Fields)
□ プロパティの利用(Checked、Items.Count、SelectedIndex、Text、Value) □ メソッドの利用(Execute、Requery、WriteLine、SubString) □ イベントハンドラの追加(EventHandler、new) □ ステートメントの利用(break) □ 制御構造構文(ループ for ( ~ ) { … }、条件分岐 switch、if ( ~ ) { … }) □ デバッグ(Debug.WriteLine) 今回の課題項目 □ イベントハンドラの追加(EventHandler、new) □ ADODB.Recordset(Fields( ).Value、Refresh) □ ADODB.Connection(Open、Execute、Close) □ SQL 文(INSERT 文:レコードを追加する文、DELETE 文:レコードを削除する文) 今回の重点項目 □ タイトルの項目を書き換えた時に削除出来ない不具合を修正する。 □ 本テーブル以外(カテゴリ、著者)のデータも操作(追加・変更・削除)出来る様に変更する。 今回の応用項目
■ プログラムリスト(追加分) ■
namespace bookshelf {
public partial class bookshelf : Form {
// 構造体の宣言
private struct Category {
public int id; public int total; }
// 構造体型の動的配列の宣言 private Category[ ] cate; ADODB.Connection cn; ADODB.Recordset rs; public bookshelf( ) { InitializeComponent( ); } // フォームが読み込まれた時の処理
private void bookshelf_Load( object sender, EventArgs e ) { // コネクションとレコードセットのインスタンス生成 cn = new ADODB.Connection( ); rs = new ADODB.Recordset( ); // コネクションの確立(データベースへの接続) cn.CursorLocation = ADODB.CursorLocationEnum.adUseClient;
cn.Open( "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=data.mdb;", "", "", 0 );
// 選択クエリに依るレコードセットの取得
rs.Open( "SELECT 本.*,著者.著者名 FROM 本,著者 WHERE 本.著者 ID=著者.ID;", cn, ADODB.CursorTypeEnum.adOpenForwardOnly, ADODB.LockTypeEnum.adLockReadOnly, 1 ); // データグリッドへの連結 dgdBookshelf.DataSource = ( msdatasrc.DataSource ) rs; // サブルーチンコール CboAddItem( ); CateUpdate( ); // イベントハンドラの追加【追加】
this.btnInsert.Click += new EventHandler( OperationSQL ); this.btnUpdate.Click += new EventHandler( OperationSQL ); this.btnDelete.Click += new EventHandler( OperationSQL ); } Structure ステートメントは、1 個又は複数の要素を持つデータ型 を宣言する。通常、レコード内の フィールド定義に用いられる。 各要素のスコープはpublicでない と、構造体の外部からアクセスす る事は出来ない。 宣言した構造体は、通常のデータ 型と同じ様に使用出来る。 オブジェクト変数のインスタンス を生成するには、New キーワード を用いて行う事が出来る。 AxDataGrid の Datasource プロ パティにレコードセットをセット する事で、データをバインドする。 クラス名と同じ名前のメソッド は、コンストラクタと呼ばれ、ク ラスのインスタンスを生成し、初 期化するメソッドで有る。 此処でprivate 宣言した変数は同 じフォームモジュール内の総ての プロシージャで値の参照と設定を 行う事が出来る。 複数のコントロールを同一のイベ ントプロシージャに関連付けるに は、新しいイベントハンドラを追 加する。 但し、イベントプロシージャ に関連付けられるプロシージ ャは、前以て記述して置く。
// データグリッドがクリックされた時の処理【追加】
private void dgdBookshelf_ClickEvent( object sender, EventArgs e ) { string s = ""; int n; // ラジオボタンで『本』が選択されて居る時丈フィールドデータを表示 if ( radBook.Checked ) {
txtID.Text = rs.Fields[ "ID" ].Value + "";
txtTitle.Text = rs.Fields[ "タイトル" ].Value + ""; txtPrice.Text = rs.Fields[ "価格" ].Value + "";
for ( int i = 0; i < cboCategory.Items.Count; i++ ) {
s = cboCategory.Items[ i ].ToString( ).Substring( 0, 3 ); n = System.Convert.ToInt32( s );
if ( System.Convert.ToInt32( rs.Fields[ "カテゴリ ID" ].Value + "") == n ) {
cboCategory.SelectedIndex = i; break; }
}
for ( int i = 0; i <cboWriter.Items.Count; i++ ) {
s = cboWriter.Items[ i ].ToString( ).Substring( 0, 3 ); n = System.Convert.ToInt32( s );
if (System.Convert.ToInt32( rs.Fields[ "著者 ID" ].Value + "") == n) { cboWriter.SelectedIndex = i; break; } } } } // コマンドボタンがクリックされた時の処理【追加】
private void OperationSQL( object sender, EventArgs e ) {
Button obj = ( Button ) sender;
int idx = System.Convert.ToInt32( obj.Tag ); string sql = "", sc = "", sw = ""; // ラジオボタンで『本』が選択されて居なければプロシージャを脱出 if ( !radBook.Checked ) return; switch ( idx ) { // ボタン(追加)がクリックされた時の処理 case 0: データグリッドに表示されて居る 左端の「 」部分をクリックする と、Recordset 内で、其のレコー ドが、カレントレコードと成る。 カレントレコードの各フィールド より其の値を取得して、コントロ ールに表示して居る。 コンボボックスのリストに対応す る数値を格納するItemData プロ パティが廃止された為、リストに ID の値を 3 桁で含めて居る。 此のプロシージャは、イベントハ ンドラを追加する前に、ボタンコ ントロールのClick イベントを基 に予め作成して置くと良い。 此の節は、case 1 の節にスルーさ れる。Visual Basic の Select 文と は異なり、各節は、break しない 限り下方の節にスルーされる。 C# は、データ型に厳格な言語で 有る。データ型の変換は、殆ど暗 黙的に行われる事はなく、明示的 に行う必要が有る。 C# では、object 型から Button 型 への変換は、暗黙的に行われない 為、明示的にキャスティングする 必要が有る。
// ボタン(変更)がクリックされた時の処理 case 1:
if ( txtTitle.Text == "") return;
if ( cboCategory.SelectedIndex < 0 ) return; if ( cboWriter.SelectedIndex < 0 ) return;
sc = cboCategory.Items[cboCategory.SelectedIndex].ToString( ).Substring(0, 3).Trim( ); sw = cboWriter.Items[cboWriter.SelectedIndex].ToString( ).Substring(0, 3).Trim( ); if ( idx == 0 )
sql = "INSERT INTO 本 (タイトル,価格,購入日,カテゴリ ID,著者 ID) VALUES(" + "'" + txtTitle.Text + "'," + txtPrice.Text + "," + "'" + DateTime.Now.ToString( ) + "'," + sc + "," + sw + ");"; else sql = "UPDATE 本 SET " + "タイトル='" + txtTitle.Text + "'," + "価格=" + txtPrice.Text + "," + "カテゴリ ID=" + sc + "," + "著者 ID=" + sw + " " +
"WHERE ID=" + txtID.Text + ";"; break;
// ボタン(削除)がクリックされた時の処理 case 2:
sql = "DELETE FROM 本 WHERE タイトル='" + txtTitle.Text + "';"; break; } // デバッグの為にイミディエイトウィンドウに SQL 文を表示 System.Diagnostics.Debug.WriteLine( sql ); // クエリの実行 object n = ( object ) 1; cn.Execute( sql, out n, 1 ); CateUpdate( ); // 表示データを更新する為に選択クエリの再実行 rs.Requery( 1 ); } 基本的にVisual Basic のコードは、行末を文の終端と看做す為、1 行毎でしか記述する事が出来な いが、C# では、セミコロン( ; )を文の終端とする為、1行のコードが長い場合は、複数行に分 割して入力する事も出来る。長い文を複数行で記述すると、画面に表示する時や印刷した時に、読 み易いコードを記述する事が出来る。 因みに、Visual Basic では、行末に行継続文字(スペースとアンダースコアの組合せ)を使用する 事で、長い文を複数行に分けて記述する事が出来る。 フィールドに設定する値は、文字 列型の場合は、シングルコーテー ションでクォートし、数値型の場 合は、其の侭記述する。 頻繁に行う処理は、名前を付け、 一纏めに仕て置き、其の処理を必 要とする箇所よりコール(呼出) する事で、対象のコードを実行す る事が出来る。 長い文は、適当な処で区切り、複 数行で結合して行くと、コードが 見易く成る。 必須項目(値要求が設定されて居 る項目)が指定されて居ない場合 は、何も仕無いで、プロシージャ を抜ける。 イミディエイトウィンドウが、表 示されない場合は、『デバッグ』メ ニューの『ウィンドウ』項目から 選択して表示する。 下方の case 節にフォールスルー させない様にするには、break 文 を記述する。
ADODB.Recordset の Open メソッド (再掲) カーソルを開くメソッド Recordset.Open( 引数1,引数2,引数3,引数4,引数5 ) ベーステーブルからのレコード、クエリ結果、又は、以前に保存されたRecordset を表すカ ーソルを開く事が出来る。 引数1には、実行するSQL ステートメント、テーブル名等を指定し、省略する事が出来る。 引数2には、有効なConnection オブジェクト変数名を指定し、省略する事が出来る。 引数3には、Recordset を開く際にプロバイダが使うカーソルタイプを指定し、省略する事が出来る。 引数4には、Recordset を開く際にプロバイダが使うロックの種類を指定し、省略する事が出来る。 引数5には、プロバイダが引数1を評価する方法を示す定数を指定し、省略する事が出来る。 ※ C# から使用する場合は、総ての引数を指定する必要が有る。 ADODB.Recordset の Requery メソッド クエリを再実行してRecordset オブジェクトのデータを更新するメソッド Recordset.Requery( 引数 ) オブジェクトの基に成るクエリ(CommandText プロパティに設定されて居るクエリ)を再 実行してRecordset オブジェクトのデータを更新する。 引数には、此の操作が作用するオプションを示すビットマスクを指定し、省略可能で有る。此のパラメ ー タ が adAsyncExecute に 設 定 さ れ て 居 る 場合 、 此 の 操 作 は 非 同 期で 実 行 さ れ 、 完 了 すると RecordsetChangeComplete イベントが発生する。 Requery メソッドを使用して、元のコマンドを再実行してデータをもう一度格納する事に依り、データ ソースからRecordset オブジェクトの内容全体を更新する。此のメソッドの呼出は、Close 及び Open メソッドを連続して呼び出すのと同じ有る。カレントレコードの編集中、又は、新規レコードを追加し て居る時に此のメソッドを呼び出すと、エラーが発生する。 Recordset オブジェクトが開いて居る間、カーソルの属性を定義するプロパティ(CursorType、 LockType、MaxRecords 等)は読み取り専用に成って居る。此の為、Requery メソッドでは、現在の カーソルしか更新する事は出来ない。カーソルのプロパティを変更して結果を参照するには、Close メ ソッドを使用してプロパティをもう一度読み取り/書き込み専用にする必要が有る。此の操作の後、プロ パティ設定を変更してOpen メソッドを呼び出し、カーソルを再度開く。 ADODB.Recordset の Close メソッド (再掲) 開いて居るオブジェクト、及び、依存する総てのオブジェクトを閉じるメソッド Recordset.Close( ) Connection オブジェクト、又は、Recordset オブジェクトを閉じて、関連するシステムリソ ースを解放する。 オブジェクトを閉じてもメモリからは削除されず、プロパティ設定を変更してもう一度開く事が出来る。 メモリからオブジェクトを完全に削除するには、オブジェクト変数をnull に設定する。 機 能 書 式 解 説 機 能 書 式 解 説 機 能 書 式 解 説
ADODB.Recordset の Fields コレクション
Recordset オブジェクトの総ての Field オブジェクトが含まれるコレクション
Recordset オブジェクトは、Recordset 内の列に対応する Field オブジェクトで構成される Fields コレクションを持つ。 Fields コレクションは、コレクション内のオブジェクト数(即ち、フィールドの数)を示す Count プ ロパティが有る。 コレクションのメンバ(要素)は、常に0 から順に番号が割り当てられる為、0 から Count プロパティ より1 小さい値迄のループを使用すると、総てのメンバにアクセスする事が出来る。 猶、Count プロパティが 0 の場合、コレクションにはオブジェクトが含まれて居ない事を意味する。 各々のメンバ(Field オブジェクト)は、下記の様に、フィールド名、又は、序数を用いて表わす事が 出来る。 フィールド名使用:Recordset.Fields( "タイトル" ) 序 数 使 用:Recordset.Fields( 1 ) 序数は、SELECT 文で指定したフィールドの順番(アスタリスク * で総てのフィールドを指定した場 合は、テーブルに定義されて居る順番)に割り当てられ、最初の要素は0 で有る。 ADODB.Recordset の Field オブジェクト 共通のデータ型を持つデータの列を表すオブジェクト
Recordset オブジェクトは、Recordset 内の列に対応する Field オブジェクトで構成される Fields コレクションを持つ。 Field オブジェクトの Value プロパティを使用して、カレントレコードのデータを設定、又は、参照す る事が出来る。但し、Field オブジェクトのコレクション、メソッド、プロパティの中には、プロバイ ダが公開する機能に依っては使用できない物も有る。 一般にField オブジェクトのコレクション、メソッド、プロパティを使用して、次の操作が可能で有る。 ・Name プロパティを使用して、フィールド名を参照する。 ・Value プロパティを使用して、フィールド内のデータ表示や変更を行う。 ・Type、Precision、NumericScale プロパティを使用して、フィールドの基本特性を参照する。 ・DefinedSize プロパティを使用して、宣言したフィールドサイズを参照する。 ・ActualSize プロパティを使用して、与えられたフィールド内の実際のデータサイズを参照する。 ・Attributes プロパティ、Properties コレクションを使用して、与えられたフィールドでサポートされ て居る機能の種類を識別する。 ・AppendChunk、GetChunk メソッドを使用して、長バイナリ型や文字型データを含むフィールドの 値を操作する。 ・プロバイダがバッチ更新をサポートして居る場合、OriginalValue、UnderlyingValue プロパティを 使用して、フィールド値の矛盾を解決する。 猶、総てのメタデータプロパティ(Name、Type、DefinedSize、Precision、NumericScale)は、Field オブジェクトのRecordset を開く前に利用出来る。動的にフォームを作成する場合に便利で有る。 機 能 解 説 機 能 解 説
Debug オブジェクトに依るデバッグ コードのデバッグに使用するメソッドとプロパティのセットを提供するオブジェクト Debug クラスのメソッドを使用し、デバッグ情報を出力し、アサーションと照らし合わせて 論理チェックを行う事で、パフォーマンスや出荷製品のサイズに影響を与える事無くコード の信頼性を高める事が出来る。 此のオブジェクトには、Write、WriteLine、WriteIf、WriteLineIf の各メソッドが用意されて居り、出 力ウィンドウに、変数等に格納されて居る値を出力する事に依り、其の値を確認する事が出来る。 猶、Visual Basic.NET 以降のエディタでは、実行時に既定で画面の下部に表示される『自動変数』の タブに、其の時の変数の値が表示され、亦、『デバッグ』メニューの『クイックウォッチ』で、任意の 変数や配列の値を確認する事も出来る。 Debug オブジェクトの WriteLine メソッドに依るデバッグ情報の出力 デバッグに付いての情報をListeners コレクションのトレースリスナに書き込メソッド Debug.WriteLine( 引数 ) Listeners コレクションのトレースリスナにメッセージを出力し、行終端記号(改行)を出 力する。 引数には、変数名、オブジェクト名、文字列を指定し、省略可能で有る。 一般的には、変数名を指定して、其の時点で、当該変数に格納されて居る値を確認する事に使用する。 猶、引数を省略した場合は、行終端記号(改行)而巳が出力される。 行終端記号を出力しない場合は、Write メソッドを使用する。Write メソッドでは、行終端記号は出力 されず、次の出力は、同一行に続けて出力される。 亦、条件に応じて、出力するか出力しないかを制御するには、WriteIf メソッドや WriteLineIf メソッ ドを使用する事も出来る。但し、WriteIf メソッドや WriteLineIf メソッドの代わりに If...Then ステー トメントを使用する事に依り、アプリケーション導入に依るパフォーマンスの低下を最小限に留める事 が出来る為、余り使用する事は無い。
猶、Debug オブジェクトには、条件をチェックし、其の条件が false の場合に、メッセージを表示する Assert メソッドも用意されて居る。Type パラメータが有効か何うかをチェックし、Type と仕て nul 参照(Visual Basic では Nothing)が渡された場合は、Assert がメッセージを出力する例を次に示す。 Public Shared Sub MyMethod(T As Type)
Debug.Assert( Not (T Is Nothing), "オブジェクトを取得出来ません!") End Sub 'MyMethod
亦、Debug オブジェクトには、デバッグ時(エディタで実行した時)に而巳、任意のエラーメッセージ を出力するFail メソッドも用意されて居る。 此等のデバッグ機能を旨く利用して、プログラム中の間違い(バグ)を素早く発見し、開発効率を向上 させる様に努める事が望ましい。 機 能 解 説 機 能 書 式 解 説
ADO と ADO.NET
Visual Basic.NET では、従来の ADO を従来の方法で使用する事も出来るが、通常、データベースにア クセスする為にADO.NET を使用する。此処では、Windows 上で動作するアプリケーションの開発に 必要な知識を中心に、ADO と ADO.NET の差異を説明する。
Visual Basic 6.0 では、データベースにアクセスする為に ADO(ActiveX Data Object)を使用するが ADO.NET は、此れを.NET Framework 用に進化させた物で有り、両者は、データアクセスの概念に大 きな差異が有る。
■ データアクセス用のコンポーネント
ADO
Visual Basic 6.0 では、データアクセス用のコンポーネントに ADO データコントロール(ADODC)が 有り、下記の機能を提供して居る。 ・データベースに接続する。 ・データベースから必要なデータを取得する。 ・フォーム上のコントロールとデータベースを連結する。 ADO.NET ADO.NET には、ADO の様な便利なコンポーネントは無い。コネクションを使用してデータベースに 接続し、データアダプタを使用して必要なデータを取得する。 ■ データの保持方法 ADO ADO では、取得したデータをレコードセットで管理する。レコードセットは、メモリ上に保持した仮 想テーブルの様な物で有る。 ADO.NET ADO.NET では、データセットに依り、データベースと同じ物をメモリ上に保持する事が出来る。デー タセットには、複数のテーブルの構造や、主キーやリレーションシップ等の属性を定義する事が出来る。 亦、実行時には、取得したデータを管理する事も出来る。 ■ レコードの扱い方 ADO ADO のレコードセットに含まれるレコードは Bookmark プロパティに依り管理されて居り、孰れかの レコードが必ず選択された状態に成って居る。因みに、レコードセットを取得した初期状態では、先頭 のレコードが選択された状態に成って居る。此のレコードをカレントレコードと呼び、亦、MoveNext メソッドやMovePrevious メソッド等に依り、カレントレコードを移動する事が出来る。 ADO.NET ADO.NET には、カレントレコードと謂う概念は無い。其の為、レコードを移動する為のメソッドも無 い。データセットに保持されたデータは、行のコレクションと仕て扱われ、各レコードは、配列の要素 番号を使用して識別する。
■ 接続方法 ADO ADO では、データベースに常時接続した状態で使用するのが基本で有る。其の為、常に最新のデータ を取得し、更新する事も可能で有るが、実際には、データの転送時以外は、データベースに接続して居 る必要が無い場合が多い。亦、データベースへの接続を維持する事で、システムリソースを消費し、パ フォーマンスが低下すると謂う負の側面も有る。 ADO.NET ADO.NET では、データセットに依り、データベースと粗同じ物をメモリ上に保持する事が出来るので データベースに接続する必要が有るのは、データセットにデータを取得する時と、データセットで変更 した内容をデータベースに転送する時丈で有る。其れ以外の時はデータベースの接続を解除し、データ セットを利用してデータベース操作を行う。 ■ ADO と ADO.NET の使い分け 新規にアプリケーションを開発する時は、ADO.NET を利用すると、豊富なツールを利用して、効率良 くアプリケーションを開発する事が出来る。ADO.NET では、データベースと常時接続する必要が無い 為、システムの負荷を軽減する事が出来る。併し、非接続型故に、大量のレコードの即時更新や、常に 最新の情報が必要な場合には、不適格で有り、此の様な場合は、常時接続型の ADO を利用すると良 い。.NET Framework でも、ADO は、サポートされて居る。
データベース フォーム フォーム データセット データベース データアダプタ コネクション
オブジェクト指向 嘗てのプログラムはシンプルなシステムの上に成り立って居た。併し、近日では、GUI(グラ フィカルユーザインターフェイス:マウスで視覚的に操作出来る仕組み)の OS(オペレーシ ョンシステム:Windows98、2000、XP、MacOS 等)が発達し、より簡単に、より複雑な 処理を行える様に成った。其れに伴い、プログラミングの手法も其れに沿う必要が有った。 ・ 開発スピードの向上 ・ 様々な機能を有するソフト開発が出来る事。 ・ 過去作業の再利用 上記の条件を満たすプログラミングの手法として『オブジェクト指向』と謂う概念が編み出 された。簡単に謂うと、『部品と部品を組み合わせてひとつの物を作る』と謂う物で有る。 VisualBasic に限らず、Java、C++、Delphi でも取り入れられて居る概念で有り、大方、考 え方に違いは無い。 概 念 図 解 オブジェクト(Object) 若しくはコントロール(Control) フォームやラベルやテキストボックス等 を指します。物体と謂う意味です。 プロパティ(Property) 私には特徴が有ります。 例えば、 名前:りんごちゃん 高さ:5cm 幅 :5cm 色 :赤色 等々 必要に応じて変更して下さい。 イベント(Event) 皆さんがマウスで私をクリックしたりする と、出来事が起こります。 例えば、
Private Sub りんごちゃん_Click() プログラム End Sub 必要に応じて何をするか言って (プログラムして)下さい。 メソッド(Method) 命令して頂ければ、 行動します。 例えば、 「動きなさい」と言って下さい。 りんごちゃん.Move いずこへ オブジェクト 1から作成する事も出来るが、既存の物に関しては使用する。VB では様々なコントロールが用 意されて居る。 プロパティ コントロールには属性が有り、デザイン上、コード上で変更、参照する事が出来る。其の数、 種類はコントロールに依り異なる。 イベント GUI では様々な操作が有る。例えば、クリック、ダブルクリック、ドラッグドロップ等。必要 に応じてイベント内にプログラムする。イベントドリブン(分割)方式と言う。 メソッド プロパティと区別が付き難いが、『=』で結ばれて居ないのが特徴。オブジェクトに対して『命 令』すると覚えると良い。