データベースの更新の実行
|
|
|
- ゆりか いりぐら
- 7 years ago
- Views:
Transcription
1 データベース データベースの更新の実行 概要 解らない事を信じて居るから 苦しむのさ 迷信は駄目だ スティービーワンダー スティービーワンダーは 勿論データベースの更新の事を謂って居た訳ではないだろうが 此の言葉は確かに此のテーマにも当て嵌る ADO.NET のデータベース更新機能は非常に強力だが.NET がベータ版だった頃に内外のニュースグループに寄せられた疑問や セミナー等で出された質問からすると 其の強力な機能を本当に理解して居る開発者は極く僅かしか居ない様で有る 此れ迄眼に仕た ADO.NET コードの中には 更新ロジックの生成を Command Builder オブジェクトに頼って居る物が少なく無い 開発者が自分で更新ロジックを用意した方が良いと謂う注記が付いて居るコードも有るが 何故そうする可きなのか 何うすれば然う出来るのかと謂う説明は殆ど無いのが現状で有る コードの仕組を聴かれると 兎に角 動けば良いじゃない と答える人が余りにも多いのではないだろうか 此処では 其の様な 迷信 紛いのコーディングからの卒業を目指し度いと思う 現に 兎に角動けば良い と謂う様なスローガンを掲げて居るテクニカルサポートも有る 残念では有るが 此れが現実で有る ADO.NET を使ってデータベースを更新する方法を理解すればする程 独自の更新ロジックを生成したり ストアドプロシージャに依って更新を実行したりする作業が快適に成る 此処では DataAdapter を使って DataSet 内の保留状態の変更内容をデータベースに反映させる方法を取り上げる 更に パフォーマンスや機能を犠牲にする事無く ツールを使って開発時間を短縮する方法 然して何んな場合に其れが有効かに付いても観て行く事にする 型指定の無い DataSet オブジェクトや厳密に型指定された DataSet オブジェクトを作成して DataAdapter オブジェクトから返されるデータを格納する方法や DataSet の内容を変更する方法に付いては 既に理解して居る物と仕て 此処では 其等を踏まえ DataAdapter オブジェクトを使って DataSet に格納されて居る変更内容をデータベースに反映させる為の基本的な方法を観て行く 例えば Northwind サンプルデータベースに 1 つの注文データが有るとする 図 10.1 は SQL Server のクエリアナライザで其の注文データを抽出する為のクエリで有る 此処で 顧客から電話が有り 此の注文データの内容を変更し度いと謂う希望を伝えられたとする 豆腐 (Tofu) は人気が無く 唐辛子ソース (Hot Pepper Sauce) は飛ぶ様に売れて居り チャイ (Chai Tea) の売れ行きも上々で有る 図 10.1 Northwind データベース内の 1 つの注文データの内容 -1-
2 DataAdapter オブジェクトを使用して クエリ結果を DataSet に格納する事に依り 顧客の注文データを DataSet に取り込む為のアプリケーションを作成出来る 亦 DataSet オブジェクトを処理する事に依り 顧客の要求に応じて DataSet 内のデータを変更する事も出来る 併し DataSet の内容を変更しても 其れ丈でデータベース内の対応する行が変更される訳では無い DataAdapter には 保留状態の変更内容をデータベースに反映させる為の Update メソッドが用意されて居る 其処で 注文データの変更をデータベースに反映するには アプリケーションで次の様なコードを使用する Visual Basic ' 注文データの内容を DataTable に取り込む Dim strconn, strsql As String strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" & _ "Initial Catalog=Northwind;Trusted_Connection=Yes;" strsql = "SELECT OrderID, ProductID, Quantity, UnitPrice " & _ "FROM [Order Details] WHERE OrderID = " & _ "ORDER BY ProductID" Dim da As New OleDbDataAdapter(strSQL, strconn) Dim tbl As New DataTable("Order Details") da.fill(tbl) ' 注文データの内容を変更する tbl.rows(0).delete( ) tbl.rows(1)("quantity") = CShort(tbl.Rows(1)("Quantity")) * 2 tbl.rows.add(new Object( ) 10503, 1, 24, 18) ' 保留状態の変更内容をデータベースに送信する Try da.update(tbl) Console.WriteLine(" 新しい変更内容がデータベースに正しく反映されました ") Catch ex As Exception Console.WriteLine("DataAdapter.Update の呼出で " & _ " 例外が発生しました :" & vbcrlf & ex.message) End Try C# // 注文データの内容を DataTable に取り込む string strconn, strsql; strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" + "Initial Catalog=Northwind;Trusted_Connection=Yes;"; strsql = "SELECT OrderID, ProductID, Quantity, UnitPrice " + "FROM [Order Details] WHERE OrderID = " + "ORDER BY ProductID"; OleDbDataAdapter da = new OleDbDataAdapter(strSQL, strconn); DataTable tbl = new DataTable("Order Details"); da.fill(tbl); // 注文データの内容を変更する -2-
3 tbl.rows[0].delete( ); tbl.rows[1]["quantity"] = (short) (tbl.rows[1]["quantity"]) * 2; tbl.rows.add(new object[] 10503, 1, 24, 18); // 保留状態の変更内容をデータベースに送信する try da.update(tbl); Console.WriteLine(" 新しい変更内容がデータベースに正しく反映されました "); catch (Exception ex) Console.WriteLine("DataAdapter.Update の呼出で例外が発生しました : n" + ex.message); 処が 此のコードは正常にコンパイルされるが 注文データへの変更はデータベースに正しく反映されず 更新には 削除された行を含む DataRow コレクションが渡された時 有効な DeleteCommand が必要です と謂う例外が出される Microsoft.NET Framework がベータ版で評価された時 此の様な例外に戸惑いを感じた開発者が大勢居た ADO 等の以前のデータアクセステクノロジには 変更内容をデータベースに自動的に反映させる機能が含まれて居た ADO.NET の場合は DataAdapter オブジェクトを使ってデータベースを更新出来るが DataAdapter に更新ロジックが自動的に組み込まれる訳では無い 其れでは ADO.NET の DataAdapter に更新ロジックを追加するには 何うすれば良いのだろうか 大きく分けて 3 つの方法が有る 1 つは 自分でコードを書くと謂う方法 もう 1 つは ADO.NET に依って更新ロジックを自動生成すると謂う方法 然して 3 つ目は Visual Studio.NET のデータアダプタ構成ウィザード等のコード生成ツールを使用すると謂う方法で有る 此処では 此の 3 つの方法を取り上げて 夫々れの長所と短所を観て行く事にする 歴史的な経緯 ADO.NET を使用して保留状態の変更内容をデータベースに適用する方法を取り上げる前に ADO.NET の前身で有る ADO では何うだったかと謂う事に少し触れて置く ADO.NET では更新ロジックが自動的に生成されないが ADO では自動生成される機能が有った ADO のカーソルエンジンが更新を 自動的に 実行する方法を観て置けば ADO.NET の開発チームが何故遣り方を変えて 開発者達が自分で更新ロジックを書く様に方針を転換したのかと謂う経緯を理解出来る 更に ADO のカーソルエンジンが変更内容をデータベースに反映させる仕組を理解すれば ADO.NET で独自の更新ロジックを生成する方法を容易に理解出来る様に成る ADO カーソルエンジンでは ADO.NET の DataSet に良く似た機能がサポートされて居る ADO のクライアントサイドの Recordset オブジェクトは オフラインのデータキャッシュと仕ての働きをしたが 其れ丈では無く データベースを更新すると謂う役目も果たした 次に示すのは 先程の注文データの内容を取り出し 其の内容を変更してから 保留状態の変更内容をデータベースに適用するコードで有る -3-
4 従来の Visual Basic と ADO 2.x Visual Basic Dim strconn As String, strsql As String strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" & _ "Initial Catalog=Northwind;Trusted_Connection=Yes;" strsql = "SELECT OrderID, ProductID, Quantity, UnitPrice " & _ "FROM [Order Details] WHERE OrderID = " & _ "ORDER BY ProductID" Dim cn As New ADODB.Connection cn.open(strconn) Dim rs As ADODB.Recordset Set rs = New ADODB.Recordset rs.cursorlocation = aduseclient rs.open strsql, cn, adopenstatic, adlockbatchoptimistic, _ adcmdtext rs.delete rs.movenext rs.fields("quantity") = 2 * rs.fields("quantity") rs.update rs.addnew rs.fields("orderid") = rs.fields("productid") = 1 rs.fields("quantity") = 24 rs.fields("unitprice") = 18 rs.update rs.updatebatch rs.close cn.close 此のコードには ADO の Recordset を使ってデータベースを更新する処理の長所と短所が良く表われて居る 此れから 其の長所と短所を観て行く事にする ADO Recordset オブジェクトを使用した更新の利点 此の方法の利点と仕て先ず挙げられるのは コードの量が少なくて済むと謂う事で有る 要は Recordset を開き 其の内容を修正し 変更内容をデータベースに適用する丈で有る 本の数行のコードで多くの処理を実行出来る訳で有る 此のコードには 更新ロジックが含まれて居ない 更新ロジックは 実行時に自動的に生成される 此れは もう 1 つの大きな利点で有る ADO では コードの中に更新ロジックを用意する必要が無い 現に 此のコードは SQL 言語の事を殆ど知らなくても書ける ADO カーソルエンジンの更新機能は 同時実行制御やロックや SQL UPDATE クエリの生成方法を知らなくても使用出来ると謂う事で有る 其の様な知識の無い開発者でも実際に動作するデータアクセスアプリケーションを作成出来ると謂う -4-
5 事は ADO の設計が優れて居る事の証拠でも有る 此れ程多くの開発者が ADO カーソルエンジンを使ってデータベースを更新して居乍ら ADO カーソルエンジンが実際に何うやって其れを処理して居るかに付いて何も知らないと謂うのは 確かに驚く可き事で有る ( 此れは皮肉でも何でも無い ) ADO Recordset オブジェクトを使用した更新の欠点 残念乍 ADO カーソルエンジンの更新機能には欠点も有る 其れは パフォーマンスが良くない事と 制御の余地が少ないと謂う事で有る ADO 1.5 の登場以来 数え切れない程多くの開発者が ADO カーソルエンジンを使ってデータベースを更新してきた事からすると 此れは大きな問題ではないのかも知れない 併し 只黙って看過ごす訳には行かないのも亦事実で有る 此等の欠点を明確に理解する為に ADO カーソルエンジンがデータベースを更新する仕組を簡単に観て置く事にする Recordset オブジェクトの UpdateBatch メソッドを呼び出すと ADO カーソルエンジンは Recordset の中から変更行を検索し 夫々れの変更行の変更内容を データベース内の対応する行を変更する為の SQL クエリに変換する データベースの内容を変更する為に 独自の UPDATE INSERT DELETE 等の SQL クエリを生成する開発者が居るが ADO カーソルエンジンも 其れと同じ様なステートメントを作成する訳で有る SQL Server には データベースに対する SQL 呼出を管理する為に SQL プロファイラと謂うツールが用意されて居る 此のツールを使って ADO カーソルエンジンがデータベースを更新する為に生成するクエリを観てみると 一連のパラメータクエリを伴った SQL Server の sp_executesql ストアドプロシージャが呼び出されて居る事が解る 此のストアドプロシージャは 次の様なクエリに相当する DELETE FROM [Order Details] WHERE OrderID = AND ProductID = 14 UPDATE [Order Details] SET Quantity = 40 WHERE OrderID = AND ProductID = 65 AND Quantity = 20 INSERT INTO [Order Details] (OrderID, ProductID, Quantity, UnitPrice) VALUES (10503, 1, 24, 18) 最初に観たクエリと コードの中で Recordset に加える変更内容とを今一度確認した後で上記のクエリを観ると 流れが良く解るかも知れない 実際に自分で書く事は出来ないと仕ても 其の意図は理解出来る筈で有る 要するに データが何処から来たのかが解って居れば Recordset 内の変更内容を SQL クエリに変換する作業は非常に簡単だと謂う事で有る 開発者に仕てみれば 此のデータが何処から来たのかは明らかだが ADO カーソルエンジンは 其の情報をどの様に入手したのだろうか ADO カーソルエンジンは クエリ結果を取り出した時点で 其の他のメタデータをデータベースから取得した 先程の UPDATE クエリを組み立てるには 基本テーブルと結果セット内の各列の名前の他に クエリで参照されて居るテーブルの主キーに関する情報が必要で有る ADO の Field オブジェクトの Properties コレクションを使用すれば 次の様にして其の種のメタデータを自分で確認出来る Visual Basic With rs.fields("quantity") Debug.Print "BaseTableName = " &.Properties("BaseTableName") Debug.Print "BaseColumnName = " &.Properties("BaseColumnName") Debug.Print "KeyColumn = " &.Properties("KeyColumn") End With -5-
6 此の時に ADO カーソルエンジンの更新機能の大きな短所と仕て最初に取り上げた点が関係して来る 詰まり パフォーマンスで有る ADO カーソルエンジンがテーブルや列や主キーのデータを取得する為のデータベースクエリを実行すれば 其の分丈パフォーマンスが低下する データアクセスコードを作成する開発者は 基本的にデータが何処から来たのかを知って居るが ADO には コードの中で其の情報を用意する為の手段が無い 従って Recordset を開く度に 此の種のメタデータを毎回データベースから取り込む事に成って仕舞う訳で有る ADO カーソルエンジンは ブラックボックス の様なテクノロジで有る 開発者が自分で更新ロジックを定義する事は出来ない 此れが実は ADO カーソルエンジンの更新機能の大きな短所と仕て 2 番目に取り上げた点で有る ADO カーソルエンジンの更新ロジック自体が素晴らしいのは確かだが 更新ロジックを制御する余地は殆ど ( 或いは 全く ) 無い Recordset の中にキャッシュされて居る更新内容をストアドプロシージャの呼出に依って実行する事も出来ない ADO カーソルエンジンが生成する更新ロジックを使用し度くない開発者は 無力な状態で放り出されて仕舞う事に成る 更新を実行する為の ADO.NET Command オブジェクトの使用 既に観た通り ADO カーソルエンジンは データベースを更新する為のパラメータクエリを作成するが ADO.NET でも 其れと同じ様なパラメータクエリを作成出来る 本稿で後述するが 其の様なパラメータ付きの Command オブジェクトを使えば ADO.NET の DataSet 内に保存されて居る変更内容をデータベースに適用出来る 此処で使用する ADO.NET の Command オブジェクトは ADO の対応機能程動的では無い 此処では流れを簡単にする為 更新を処理する Command 挿入を処理する Command 削除を処理する Command を夫々れ 1 つ宛作成する 其等のオブジェクトのベースに成るのは 次のパラメータクエリで有る UPDATE [Order Details] SET OrderID =?, ProductID =?, Quantity =?, UnitPrice =? WHERE OrderID =? AND ProductID =? AND Quantity =? AND UnitPrice =? INSERT INTO [Order Details] (OrderID, ProductID, Quantity, UnitPrice) VALUES (?,?,?,?) DELETE FROM [Order Details] WHERE OrderID =? AND ProductID =? AND Quantity =? AND UnitPrice =? 此の UPDATE クエリと INSERT クエリでは 元のクエリに含まれる各列の新しい値をデータベースに適用するが 特に UPDATE クエリでは 元のクエリの各列を参照する為に WHERE 句を使って居る 此の方法には 長所も有れば短所も有る 其の点に付いては 後述する 次に示すのは 其の 3 つのパラメータ付きの Command オブジェクトを作成するコードで有る 夫々れのコードは cn と謂う OleDbConnection オブジェクトが外部で定義されて居る事を前提と仕て居る Visual Basic Private Function CreateUpdateCommand( ) As OleDbCommand Dim strsql As String strsql = "UPDATE [Order Details] " & _ " SET OrderID =?, ProductID =?, " & _ " Quantity =?, UnitPrice =? " & _ " WHERE OrderID =? AND ProductID =? AND " & _ " Quantity =? AND UnitPrice =?" Dim cmd As New OleDbCommand(strSQL, cn) Dim pc As OleDbParameterCollection = cmd.parameters -6-
7 pc.add("orderid_new", OleDbType.Integer) pc.add("productid_new", OleDbType.Integer) pc.add("quantity_new", OleDbType.SmallInt) pc.add("unitprice_new", OleDbType.Currency) pc.add("orderid_orig", OleDbType.Integer) pc.add("productid_orig", OleDbType.Integer) pc.add("quantity_orig", OleDbType.SmallInt) pc.add("unitprice_orig", OleDbType.Currency) Return cmd End Function Private Function CreateInsertCommand( ) As OleDbCommand Dim strsql As String strsql = "INSERT INTO [Order Details] " & _ " (OrderID, ProductID, Quantity, UnitPrice) " & _ " VALUES (?,?,?,?)" Dim cmd As New OleDbCommand(strSQL, cn) Dim pc As OleDbParameterCollection = cmd.parameters pc.add("orderid", OleDbType.Integer) pc.add("productid", OleDbType.Integer) pc.add("quantity", OleDbType.SmallInt) pc.add("unitprice", OleDbType.Currency) Return cmd End Function Private Function CreateDeleteCommand( ) As OleDbCommand Dim strsql As String strsql = "DELETE FROM [Order Details] " & _ " WHERE OrderID =? AND ProductID =? AND " & _ " Quantity =? AND UnitPrice =?" Dim cmd As New OleDbCommand(strSQL, cn) Dim pc As OleDbParameterCollection = cmd.parameters pc.add("orderid", OleDbType.Integer) pc.add("productid", OleDbType.Integer) pc.add("quantity", OleDbType.SmallInt) pc.add("unitprice", OleDbType.Currency) Return cmd End Function C# static OleDbCommand CreateUpdateCommand( ) string strsql; strsql = "UPDATE [Order Details] " + " SET OrderID =?, ProductID =?, " + " Quantity =?, UnitPrice =? " + " WHERE OrderID =? AND ProductID =? AND " + " Quantity =? AND UnitPrice =?"; OleDbCommand cmd = new OleDbCommand(strSQL, cn); OleDbParameterCollection pc = cmd.parameters; pc.add("orderid_new", OleDbType.Integer); pc.add("productid_new", OleDbType.Integer); pc.add("quantity_new", OleDbType.SmallInt); -7-
8 pc.add("unitprice_new", OleDbType.Currency); pc.add("orderid_orig", OleDbType.Integer); pc.add("productid_orig", OleDbType.Integer); pc.add("quantity_orig", OleDbType.SmallInt); pc.add("unitprice_orig", OleDbType.Currency); return cmd; static OleDbCommand CreateInsertCommand( ) string strsql; strsql = "INSERT INTO [Order Details] " + " (OrderID, ProductID, Quantity, UnitPrice) " + " VALUES (?,?,?,?)"; OleDbCommand cmd = new OleDbCommand(strSQL, cn); OleDbParameterCollection pc = cmd.parameters; pc.add("orderid", OleDbType.Integer); pc.add("productid", OleDbType.Integer); pc.add("quantity", OleDbType.SmallInt); pc.add("unitprice", OleDbType.Currency); return cmd; static OleDbCommand CreateDeleteCommand( ) string strsql; strsql = "DELETE FROM [Order Details] " + " WHERE OrderID =? AND ProductID =? AND " + " Quantity =? AND UnitPrice =?"; OleDbCommand cmd = new OleDbCommand(strSQL, cn); OleDbParameterCollection pc = cmd.parameters; pc.add("orderid", OleDbType.Integer); pc.add("productid", OleDbType.Integer); pc.add("quantity", OleDbType.SmallInt); pc.add("unitprice", OleDbType.Currency); return cmd; 此の様なパラメータ付きの Command オブジェクトを使ってデータベースを更新する処理は 単純明快で有る 先ず必要なのは DataTable 内の変更行を特定し 其の各行の変更の種類 ( 更新 / 挿入 / 削除 ) を確認し 各行の内容に基づいて 夫々れ該当するコマンドのパラメータの値を設定する事で有る Command 内に格納されて居るクエリを実行する為に ExecuteNonQuery メソッドを呼び出したら 其のメソッドの戻り値から 更新が正常に実行されたか何うかを確認出来る 保留状態の変更内容をデータベースに正しく反映出来た場合は DataRow の AcceptChanges メソッドを呼び出し 更新が失敗した場合は 其の失敗を通知するテキストを DataRow オブジェクトの RowError プロパティに設定する Visual Basic Private Sub SubmitChangesByHand( ) Dim cmdupdate As OleDbCommand = CreateUpdateCommand( ) Dim cmdinsert As OleDbCommand = CreateInsertCommand( ) Dim cmddelete As OleDbCommand = CreateDeleteCommand( ) Dim row As DataRow -8-
9 Dim introwsaffected As Integer Dim dvrs As DataViewRowState dvrs = DataViewRowState.ModifiedCurrent _ Or DataViewRowState.Deleted Or DataViewRowState.Added For Each row In tbl.select("", "", dvrs) Select Case row.rowstate Case DataRowState.Modified introwsaffected = SubmitUpdate(row, cmdupdate) Case DataRowState.Added introwsaffected = SubmitInsert(row, cmdinsert) Case DataRowState.Deleted introwsaffected = SubmitDelete(row, cmddelete) End Select If introwsaffected = 1 Then row.acceptchanges( ) Else row.rowerror = " 更新に失敗しました " End If Next row End Sub Private Function SubmitUpdate(ByVal row As DataRow, _ ByVal cmd As OleDbCommand) As Integer Dim pc As OleDbParameterCollection = cmd.parameters pc("orderid_new").value = row("orderid") pc("productid_new").value = row("productid") pc("quantity_new").value = row("quantity") pc("unitprice_new").value = row("unitprice") pc("orderid_orig").value = row("orderid", _ DataRowVersion.Original) pc("quantity_orig").value = row("quantity", _ DataRowVersion.Original) pc("productid_orig").value = row("productid", _ DataRowVersion.Original) pc("unitprice_orig").value = row("unitprice", _ DataRowVersion.Original) Return cmd.executenonquery End Function Private Function SubmitInsert(ByVal row As DataRow, _ ByVal cmd As OleDbCommand) As Integer Dim pc As OleDbParameterCollection = cmd.parameters pc("orderid").value = row("orderid") pc("productid").value = row("productid") pc("quantity").value = row("quantity") pc("unitprice").value = row("unitprice") Return cmd.executenonquery End Function Private Function SubmitDelete(ByVal row As DataRow, _ ByVal cmd As OleDbCommand) As Integer Dim pc As OleDbParameterCollection = cmd.parameters pc("orderid").value = row("orderid", DataRowVersion.Original) pc("productid").value = row("productid", DataRowVersion.Original) pc("quantity").value = row("quantity", DataRowVersion.Original) pc("unitprice").value = row("unitprice", DataRowVersion.Original) Return cmd.executenonquery End Function -9-
10 C# static void SubmitChangesByHand( ) OleDbCommand cmdupdate = CreateUpdateCommand( ); OleDbCommand cmdinsert = CreateInsertCommand( ); OleDbCommand cmddelete = CreateDeleteCommand( ); DataViewRowState dvrs; dvrs = DataViewRowState.ModifiedCurrent DataViewRowState.Deleted DataViewRowState.Added; int introwsaffected = 0; foreach (DataRow row in tbl.select("", "", dvrs)) switch (row.rowstate) case DataRowState.Modified: introwsaffected = SubmitUpdate(row, cmdupdate); break; case DataRowState.Added: introwsaffected = SubmitInsert(row, cmdinsert); break; case DataRowState.Deleted: introwsaffected = SubmitDelete(row, cmddelete); break; if (introwsaffected == 1) row.acceptchanges( ); else row.rowerror = " 更新に失敗しました "; static int SubmitUpdate(DataRow row, OleDbCommand cmd) OleDbParameterCollection pc = cmd.parameters; pc["orderid_new"].value = row["orderid"]; pc["productid_new"].value = row["productid"]; pc["quantity_new"].value = row["quantity"]; pc["unitprice_new"].value = row["unitprice"]; pc["orderid_orig"].value = row["orderid", DataRowVersion.Original]; pc["productid_orig"].value = row["productid", DataRowVersion.Original]; pc["quantity_orig"].value = row["quantity", DataRowVersion.Original]; pc["unitprice_orig"].value = row["unitprice", DataRowVersion.Original]; return cmd.executenonquery( ); static int SubmitInsert(DataRow row, OleDbCommand cmd) OleDbParameterCollection pc = cmd.parameters; pc["orderid"].value = row["orderid"]; pc["productid"].value = row["productid"]; pc["quantity"].value = row["quantity"]; pc["unitprice"].value = row["unitprice"]; return cmd.executenonquery( ); -10-
11 static int SubmitDelete(DataRow row, OleDbCommand cmd) OleDbParameterCollection pc = cmd.parameters; pc["orderid"].value = row["orderid", DataRowVersion.Original]; pc["productid"].value = row["productid", DataRowVersion.Original]; pc["quantity"].value = row["quantity", DataRowVersion.Original]; pc["unitprice"].value = row["unitprice", DataRowVersion.Original]; return cmd.executenonquery( ); 此のコードでは DataTable オブジェクトの Select メソッドを使って変更行をループ処理して居る 此処で DataTable オブジェクトの Rows コレクション内の各項目を調べる為に For ループや For Each ループを使用しなかった事には理由が有る 保留状態の削除操作をデータベースに正しく反映出来た場合 其の DataRow の AcceptChanges メソッドを呼び出すと 其の項目が親コレクションから完全に除去される 其の点 Select メソッドから返されるのは DataRow オブジェクトの配列だが 其の配列に入って居るのは基本的に 変更行へのポインタなので DataTable オブジェクトの DataRow オブジェクトのコレクションから項目を完全に除去して仕舞っても コードは正常に実行される事に成る 扨て 此のコードを実際に使って観る事にする 此の後に示すのは 注文データの詳細を DataTable に取り込み 注文データの内容を変更してから データベースに変更内容を適用するコードで有る 此のコードからも解る通り 此れ迄に観てきたコードの断片では 保留状態の変更内容をデータベースに正しく適用出来る 此処では 本稿の初めの方で定義したプロシージャを利用して居る 亦 DataTable の現在の内容を書き出すプロシージャも含まれて居るので 注文の内容が正しく更新されたか何うかを確認出来る 更に 注文の元の内容を作成し直す為の ResetOrder プロシージャも組み込まれて居るので 此のコードは何度でも実行出来る様に成って居る Visual Basic Dim cn As OleDbConnection Dim da As OleDbDataAdapter Dim tbl As DataTable = GenTable( ) Sub Main( ) Dim strconn, strsql As String strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" & _ "Initial Catalog=Northwind;Trusted_Connection=Yes;" strsql = "SELECT OrderID, ProductID, Quantity, UnitPrice " & _ "FROM [Order Details] WHERE OrderID = " & _ "ORDER BY ProductID" cn = New OleDbConnection(strConn) da = New OleDbDataAdapter(strSQL, cn) cn.open( ) ResetOrder( ) da.fill(tbl) DisplayOrder(" データベースの元の内容 ") ModifyOrder( ) DisplayOrder("DataSet 内の変更データ ") SubmitChangesByHand( ) -11-
12 tbl.clear( ) da.fill(tbl) DisplayOrder(" データベースの新しい内容 ") cn.close( ) End Sub Private Sub ModifyOrder( ) Dim row As DataRow row = tbl.rows(0) row.delete( ) row = tbl.rows(1) row("quantity") = CType(row("Quantity"), Int16) * 2 row = tbl.newrow row("orderid") = row("productid") = 1 row("quantity") = 24 row("unitprice") = 18.0 tbl.rows.add(row) End Sub Public Sub DisplayOrder(ByVal strstatus As String) Dim row As DataRow Dim col As DataColumn Console.WriteLine(strStatus) Console.WriteLine(" OrderID ProductID " & _ "Quantity UnitPrice") For Each row In tbl.select("", "ProductID") For Each col In tbl.columns Console.Write(vbTab & row(col) & vbtab) Next Console.WriteLine( ) Next Console.WriteLine( ) End Sub Private Sub ResetOrder( ) Dim strsql As String Dim cmd As OleDbCommand = cn.createcommand( ) strsql = "DELETE FROM [Order Details] WHERE OrderID = 10503" cmd.commandtext = strsql cmd.executenonquery( ) strsql = "INSERT INTO [Order Details] " & _ " (OrderID, ProductID, Quantity, UnitPrice) " & _ " VALUES (10503, 14, 70, 23.25) " cmd.commandtext = strsql cmd.executenonquery( ) strsql = "INSERT INTO [Order Details] " & _ " (OrderID, ProductID, Quantity, UnitPrice) " & _ " VALUES (10503, 65, 20, 21.05)" cmd.commandtext = strsql cmd.executenonquery( ) End Sub Public Function GenTable( ) As DataTable Dim tbl As New DataTable("Order Details") Dim col As DataColumn With tbl.columns -12-
13 col =.Add("OrderID", GetType(Integer)) col.allowdbnull = False col =.Add("ProductID", GetType(Integer)) col.allowdbnull = False col =.Add("Quantity", GetType(Int16)) col.allowdbnull = False col =.Add("UnitPrice", GetType(Decimal)) col.allowdbnull = False End With tbl.primarykey = New DataColumn( ) tbl.columns("orderid"), _ tbl.columns("productid") Return tbl End Function C# static OleDbConnection cn; static OleDbDataAdapter da; static DataTable tbl; static void Main(string[] args) string strconn, strsql; strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" + "Initial Catalog=Northwind;Trusted_Connection=Yes;"; strsql = "SELECT OrderID, ProductID, Quantity, UnitPrice " + "FROM [Order Details] WHERE OrderID = " + "ORDER BY ProductID"; cn = new OleDbConnection(strConn); da = new OleDbDataAdapter(strSQL, cn); tbl = GenTable( ); cn.open( ); ResetOrder( ); da.fill(tbl); DisplayOrder(" データベースの元の内容 "); ModifyOrder( ); DisplayOrder("DataSet 内の変更データ "); SubmitChangesByHand( ); tbl.clear( ); da.fill(tbl); DisplayOrder(" データベースの新しい内容 "); cn.close( ); static void ModifyOrder( ) DataRow row; row = tbl.rows[0]; row.delete( ); row = tbl.rows[1]; row["quantity"] = (Int16) row["quantity"] * 2; row = tbl.newrow( ); row["orderid"] = 10503; row["productid"] = 1; row["quantity"] = 24; row["unitprice"] = 18.0; tbl.rows.add(row); -13-
14 static void DisplayOrder(string strstatus) Console.WriteLine(strStatus); Console.WriteLine(" OrderID ProductID " + "Quantity UnitPrice"); foreach(datarow row in tbl.select("", "ProductID")) foreach(datacolumn col in tbl.columns) Console.Write(" t" + row[col] + " t"); Console.WriteLine( ); Console.WriteLine( ); static void ResetOrder( ) string strsql; OleDbCommand cmd = cn.createcommand( ); strsql = "DELETE FROM [Order Details] WHERE OrderID = 10503"; cmd.commandtext = strsql; cmd.executenonquery( ); strsql = "INSERT INTO [Order Details] " + " (OrderID, ProductID, Quantity, UnitPrice) " + " VALUES (10503, 14, 70, 23.25) " ; cmd.commandtext = strsql; cmd.executenonquery( ); strsql = "INSERT INTO [Order Details] " + " (OrderID, ProductID, Quantity, UnitPrice) " + " VALUES (10503, 65, 20, 21.05)"; cmd.commandtext = strsql; cmd.executenonquery( ); static DataTable GenTable( ) DataTable tbl = new DataTable("Order Details"); DataColumn col; col = tbl.columns.add("orderid", typeof(int)); col.allowdbnull = false; col = tbl.columns.add("productid", typeof(int)); col.allowdbnull = false; col = tbl.columns.add("quantity", typeof(int16)); col.allowdbnull = false; col = tbl.columns.add("unitprice", typeof(decimal)); col.allowdbnull = false; tbl.primarykey = new DataColumn[] tbl.columns["orderid"], tbl.columns["productid"]; return tbl; 此の様に仕て 保留状態の変更内容をデータベースに適用する為の大量のコードを記述した パラメータ付きの Command オブジェクトを生成する為のコードは 最初に観たクエリに固有の物だが SubmitChangesByHand プロシージャのコードは 汎用的で有る 詰まり DataTable 内にキャッシュされて居る変更行を調べ 変更の有った各 DataRow 内に保存されて居る変更の種類を確認し 其の保留状態の変更内容をデータベースに反映させるクエリを実行する為の関数を呼び出し 其の関数の戻 -14-
15 り値に基づいて DataRow の状態を設定する汎用的なコードで有る 実を謂えば 此れ迄の作業は DataAdapter オブジェクトから得られる更新機能を自分で作成したと謂う丈の話で有る 其の DataAdapter オブジェクトの更新機能を次に観て観る事にする 更新を実行する為の ADO.NET DataAdapter オブジェクトの使用 DataAdapter オブジェクトを使用してクエリ結果を DataTable に取り込む事は DataAdapter の機能の半分に過ぎない 此のオブジェクトには DataSet 内の保留状態の変更内容をデータベースに適用する機能も有る DataAdapter オブジェクトに依り変更内容をデータベースに適用する為の更新ロジックを生成する方法と仕ては 次の 3 つが有る コードを使用して DataAdapter オブジェクトを手作業で設定する方法 実行時に CommandBuilder を使用する方法 デザイン時にデータアダプタ構成ウィザードを使用する方法 此の 3 つの方法には 夫々れ長所と短所が有る 此れから 夫々れの方法を詳しく観て行く事にする DataAdapter オブジェクトの手動での構成 DataAdapter オブジェクトには Command オブジェクトを指定する為のプロパティが 4 つ用意されて居る 既に観た通り SelectCommand プロパティには DataAdapter が DataTable にデータを取り込む為の Command を指定出来る 一方 其の他の 3 つのプロパティ (UpdateCommand InsertCommand DeleteCommand) には DataAdapter が保留状態の変更内容をデータベースに適用する為の Command オブジェクトを指定出来る 此のアーキテクチャは ADO のオブジェクトモデルからの大きな変更点で有る 謎めいた ブラックボックス の様なテクノロジは最早無い DataAdapter が保留状態の変更内容をデータベースに適用する方法を開発者自身が指定し DataAdapter が使用する Command オブジェクトを開発者自身が用意する必要が有る訳で有る DataAdapter オブジェクトの Update メソッドは 非常に柔軟で有る パラメータと仕て DataSet DataSet とテーブル名の組み合わせ DataTable DataRow オブジェクトの配列の孰れかを指定出来る DataAdapter オブジェクトの Update メソッドを何んな方法で呼び出すにしても DataAdapter は 保留状態の変更内容を指定の Command に依って実行しようとする 先程の SubmitChangesByHand プロシージャの中で実行して居た総ての作業は DataAdapter オブジェクトの Update メソッドを 1 回呼び出す丈で実現出来る パラメータのバインドの概要 先程の SubmitChangesByHand プロシージャは 其れ程複雑ではなく 其れ程多くの事を実行する訳でも無い 寧ろ 面倒な作業は SubmitUpdate SubmitInsert SubmitDelete の孰れかの関数に任せて居た 其等の関数は 変更行の内容に基づいて 夫々れ該当するクエリのパラメータ値を設定する DataAdapter を使って 保留状態の変更内容をデータベースに適用する場合も 同じパラメータクエリを使用する -15-
16 UPDATE [Order Details] SET OrderID =?, ProductID =?, Quantity =?, UnitPrice =? WHERE OrderID =? AND ProductID =? AND Quantity =? AND UnitPrice =? INSERT INTO [Order Details] (OrderID, ProductID, Quantity, UnitPrice) VALUES (?,?,?,?) DELETE FROM [Order Details] WHERE OrderID =? AND ProductID =? AND Quantity =? AND UnitPrice =? 但し DataAdapter オブジェクトの Command オブジェクトに Parameter オブジェクトを追加する場合は ADO.NET の Parameter オブジェクトの 2 つのプロパティ ( 詰まり DataAdapter に依る更新用に特化した SourceColumn と SourceVersion) を使用する事に成る 此の 2 つのプロパティは基本的に 1 つの Parameter を DataTable 内の 1 つの DataColumn にバインドする DataAdapter は 此の 2 つのプロパティを使って Parameter オブジェクトの Value プロパティの設定方法を決定してから クエリを実行する ( 此れは SubmitUpdate SubmitInsert SubmitDelete の各関数を使った場合と同じ流れで有る ) 図 10.2 を参照され度い 図 10.2 Parameter オブジェクトと DataColumn オブジェクトのバインド 次に示すのは パラメータ付きの Command オブジェクトを作成した上で Parameter オブジェクトの SourceColumn プロパティと SourceVersion プロパティを設定するコードで有る SourceVersion プロパティの既定値は DataRowVersion.Current なので 此のプロパティを明示的に設定する必要が有るのは Parameter オブジェクトを列の元の値にバインドする場合に限られる Visual Basic Private Function CreateDataAdapterUpdateCommand( ) As OleDbCommand Dim strsql As String strsql = "UPDATE [Order Details] " & _ " SET OrderID =?, ProductID =?, " & _ " Quantity =?, UnitPrice =? " & _ " WHERE OrderID =? AND ProductID =? AND " & _ " Quantity =? AND UnitPrice =?" Dim cmd As New OleDbCommand(strSQL, cn) -16-
17 Dim pc As OleDbParameterCollection = cmd.parameters pc.add("orderid_new", OleDbType.Integer, 0, "OrderID") pc.add("productid_new", OleDbType.Integer, 0, "ProductID") pc.add("quantity_new", OleDbType.SmallInt, 0, "Quantity") pc.add("unitprice_new", OleDbType.Currency, 0, "UnitPrice") Dim param As OleDbParameter param = pc.add("orderid_orig", OleDbType.Integer, 0, "OrderID") param.sourceversion = DataRowVersion.Original param = pc.add("productid_orig", OleDbType.Integer, 0, _ "ProductID") param.sourceversion = DataRowVersion.Original param = pc.add("quantity_orig", OleDbType.SmallInt, 0, _ "Quantity") param.sourceversion = DataRowVersion.Original param = pc.add("unitprice_orig", OleDbType.Currency, 0, _ "UnitPrice") param.sourceversion = DataRowVersion.Original Return cmd End Function Private Function CreateDataAdapterInsertCommand( ) As OleDbCommand Dim strsql As String strsql = "INSERT INTO [Order Details] " & _ " (OrderID, ProductID, Quantity, UnitPrice) " & _ " VALUES (?,?,?,?)" Dim cmd As New OleDbCommand(strSQL, cn) Dim pc As OleDbParameterCollection = cmd.parameters pc.add("orderid", OleDbType.Integer, 0, "OrderID") pc.add("productid", OleDbType.Integer, 0, "ProductID") pc.add("quantity", OleDbType.SmallInt, 0, "Quantity") pc.add("unitprice", OleDbType.Currency, 0, "UnitPrice") Return cmd End Function Private Function CreateDataAdapterDeleteCommand( ) As OleDbCommand Dim strsql As String strsql = "DELETE FROM [Order Details] " & _ " WHERE OrderID =? AND ProductID =? AND " & _ " Quantity =? AND UnitPrice =?" Dim cmd As New OleDbCommand(strSQL, cn) Dim pc As OleDbParameterCollection = cmd.parameters Dim param As OleDbParameter param = pc.add("orderid", OleDbType.Integer, 0, "OrderID") param.sourceversion = DataRowVersion.Original param = pc.add("productid", OleDbType.Integer, 0, "ProductID") param.sourceversion = DataRowVersion.Original param = pc.add("quantity", OleDbType.SmallInt, 0, "Quantity") param.sourceversion = DataRowVersion.Original param = pc.add("unitprice", OleDbType.Currency, 0, "UnitPrice") param.sourceversion = DataRowVersion.Original Return cmd End Function -17-
18 C# static OleDbCommand CreateDataAdapterUpdateCommand( ) string strsql; strsql = "UPDATE [Order Details] " + " SET OrderID =?, ProductID =?, " + " Quantity =?, UnitPrice =? " + " WHERE OrderID =? AND ProductID =? AND " + " Quantity =? AND UnitPrice =?"; OleDbCommand cmd = new OleDbCommand(strSQL, cn); OleDbParameterCollection pc = cmd.parameters; pc.add("orderid_new", OleDbType.Integer, 0, "OrderID"); pc.add("productid_new", OleDbType.Integer, 0, "ProductID"); pc.add("quantity_new", OleDbType.SmallInt, 0, "Quantity"); pc.add("unitprice_new", OleDbType.Currency, 0, "UnitPrice"); OleDbParameter param; param = pc.add("orderid_orig", OleDbType.Integer, 0, "OrderID"); param.sourceversion = DataRowVersion.Original; param = pc.add("productid_orig", OleDbType.Integer, 0, "ProductID"); param.sourceversion = DataRowVersion.Original; param = pc.add("quantity_orig", OleDbType.SmallInt, 0, "Quantity"); param.sourceversion = DataRowVersion.Original; param = pc.add("unitprice_orig", OleDbType.Currency, 0, "UnitPrice"); param.sourceversion = DataRowVersion.Original; return cmd; static OleDbCommand CreateDataAdapterInsertCommand( ) string strsql; strsql = "INSERT INTO [Order Details] " + " (OrderID, ProductID, Quantity, UnitPrice) " + " VALUES (?,?,?,?)"; OleDbCommand cmd = new OleDbCommand(strSQL, cn); OleDbParameterCollection pc = cmd.parameters; pc.add("orderid", OleDbType.Integer, 0, "OrderID"); pc.add("productid", OleDbType.Integer, 0, "ProductID"); pc.add("quantity", OleDbType.SmallInt, 0, "Quantity"); pc.add("unitprice", OleDbType.Currency, 0, "UnitPrice"); return cmd; static OleDbCommand CreateDataAdapterDeleteCommand( ) string strsql; strsql = "DELETE FROM [Order Details] " + " WHERE OrderID =? AND ProductID =? AND " + " Quantity =? AND UnitPrice =?"; OleDbCommand cmd = new OleDbCommand(strSQL, cn); OleDbParameter param; OleDbParameterCollection pc = cmd.parameters; -18-
19 param = pc.add("orderid", OleDbType.Integer, 0, "OrderID"); param.sourceversion = DataRowVersion.Original; param = pc.add("productid", OleDbType.Integer, 0, "ProductID"); param.sourceversion = DataRowVersion.Original; param = pc.add("quantity", OleDbType.SmallInt, 0, "Quantity"); param.sourceversion = DataRowVersion.Original; param = pc.add("unitprice", OleDbType.Currency, 0, "UnitPrice"); param.sourceversion = DataRowVersion.Original; return cmd; 此れで SubmitChangesByHand SubmitUpdate SubmitInsert SubmitDelete の各プロシージャの代わりに 次のコードを使用出来る様に成る Visual Basic Private Sub SubmitChangesViaDataAdapter( ) da.updatecommand = CreateDataAdapterUpdateCommand( ) da.insertcommand = CreateDataAdapterInsertCommand( ) da.deletecommand = CreateDataAdapterDeleteCommand( ) da.update(tbl) End Sub C# static void SubmitChangesViaDataAdapter( ) da.updatecommand = CreateDataAdapterUpdateCommand( ); da.insertcommand = CreateDataAdapterInsertCommand( ); da.deletecommand = CreateDataAdapterDeleteCommand( ); da.update(tbl); 更新を実行する為のストアドプロシージャの使用 ADO を使ってデータベースのデータを抽出して居た開発者達の間では Recordset オブジェクトの UpdateBatch メソッドがストアドプロシージャに依るデータベース更新に対応して居ないと謂う不満が有った 既に観た通り DataAdapter を使用すれば 開発者が自分で更新ロジックを定義出来る 先程のコードも然うだが 自分で Command オブジェクトを作成し 保留状態の変更内容をデータベースに反映させる為の DataAdapter に其の Command オブジェクトを指定すれば良い訳で有る 先程と同じ様なコードを使えば ストアドプロシージャに依るデータベース更新も可能に成る 先ず Northwind データベースの中に Order Details テーブルの行を変更 / 挿入 / 削除するストアドプロシージャを定義する必要が有る SQL クエリアナライザに次のコードをペーストして実行すれば 其のストアドプロシージャを作成出来る ( 此のストアドプロシージャをコードから呼び出す訳で有る ) 但し MSDE しかない場合は SQL クエリアナライザを使用出来ない 其の場合は CreateSprocs と謂うプロシージャ ( 後からコードの中でも使用する ) を呼び出して ストアドプロシージャを作成する事に成る -19-
20 USE Northwind GO CREATE PROCEDURE spupdatedetail money) AS UPDATE [Order Details] SET OrderID ProductID Quantity UnitPrice WHERE OrderID AND ProductID AND Quantity AND UnitPrice GO CREATE PROCEDURE spinsertdetail (@OrderID money) AS INSERT INTO [Order Details] (OrderID, ProductID, Quantity, UnitPrice) @UnitPrice) GO CREATE PROCEDURE spdeletedetail (@OrderID money) AS DELETE FROM [Order Details] WHERE OrderID AND ProductID AND Quantity AND UnitPrice 此れで Order Details テーブルを更新する為のストアドプロシージャを作成出来た 次に DataAdapter オブジェクトの Update メソッドを呼び出した時に自動的に其のストアドプロシージャを呼び出す Command オブジェクトを作成する 次に示すコードには 其のストアドプロシージャの呼出を指定した Command オブジェクトを作成する関数を組み込んで有る 亦 データベースの中に其のストアドプロシージャを作成する為のプロシージャも含めた 後は 新しい Command オブジェクトを DataAdapter に結び付ける丈で有る 其の為に組み込んで有るのが SubmitChangesViaStoredProcedures プロシージャで有る Visual Basic Private Sub SubmitChangesViaStoredProcedures( ) da.updatecommand = CreateUpdateViaSPCommand( ) da.insertcommand = CreateInsertViaSPCommand( ) da.deletecommand = CreateDeleteViaSPCommand( ) da.update(tbl) End Sub Private Function CreateUpdateViaSPCommand( ) As OleDbCommand Dim cmd As New OleDbCommand("spUpdateDetail", cn) -20-
21 cmd.commandtype = CommandType.StoredProcedure Dim pc As OleDbParameterCollection = cmd.parameters pc.add("orderid_new", OleDbType.Integer, 0, "OrderID") pc.add("productid_new", OleDbType.Integer, 0, "ProductID") pc.add("quantity_new", OleDbType.SmallInt, 0, "Quantity") pc.add("unitprice_new", OleDbType.Currency, 0, "UnitPrice") Dim param As OleDbParameter param = pc.add("orderid_orig", OleDbType.Integer, 0, "OrderID") param.sourceversion = DataRowVersion.Original param = pc.add("productid_orig", OleDbType.Integer, 0, _ "ProductID") param.sourceversion = DataRowVersion.Original param = pc.add("quantity_orig", OleDbType.SmallInt, 0, _ "Quantity") param.sourceversion = DataRowVersion.Original param = pc.add("unitprice_orig", OleDbType.Currency, 0, _ "UnitPrice") param.sourceversion = DataRowVersion.Original Return cmd End Function Private Function CreateInsertViaSPCommand( ) As OleDbCommand Dim cmd As New OleDbCommand("spInsertDetail", cn) cmd.commandtype = CommandType.StoredProcedure Dim pc As OleDbParameterCollection = cmd.parameters pc.add("orderid", OleDbType.Integer, 0, "OrderID") pc.add("productid", OleDbType.Integer, 0, "ProductID") pc.add("quantity", OleDbType.SmallInt, 0, "Quantity") pc.add("unitprice", OleDbType.Currency, 0, "UnitPrice") Return cmd End Function Private Function CreateDeleteViaSPCommand( ) As OleDbCommand Dim cmd As New OleDbCommand("spDeleteDetail", cn) cmd.commandtype = CommandType.StoredProcedure Dim pc As OleDbParameterCollection = cmd.parameters Dim param As OleDbParameter param = pc.add("orderid", OleDbType.Integer, 0, "OrderID") param.sourceversion = DataRowVersion.Original param = pc.add("productid", OleDbType.Integer, 0, "ProductID") param.sourceversion = DataRowVersion.Original param = pc.add("quantity", OleDbType.SmallInt, 0, "Quantity") param.sourceversion = DataRowVersion.Original param = pc.add("unitprice", OleDbType.Currency, 0, "UnitPrice") param.sourceversion = DataRowVersion.Original Return cmd End Function Private Sub CreateSprocs( ) Dim cmd As OleDbCommand = cn.createcommand Dim strsql As String -21-
22 strsql = "CREATE PROCEDURE spupdatedetail " & vbcrlf & _ " (@OrderID_New int, " & vbcrlf & _ smallint, " & vbcrlf & _ money, " & vbcrlf & _ int, " & vbcrlf & _ int, " & vbcrlf & _ smallint, " & vbcrlf & _ money) " & vbcrlf & _ "AS " & vbcrlf & _ "UPDATE [Order Details] " & vbcrlf & _ " SET OrderID " & vbcrlf & _ " ProductID " & vbcrlf & _ " Quantity " & vbcrlf & _ " UnitPrice " & vbcrlf & _ " WHERE OrderID AND " & vbcrlf & _ " ProductID AND " & vbcrlf & _ " Quantity AND " & vbcrlf & _ " UnitPrice cmd.commandtext = strsql cmd.executenonquery( ) strsql = "CREATE PROCEDURE spinsertdetail " & vbcrlf & _ " (@OrderID int, " & vbcrlf & _ money) " & vbcrlf & _ "AS " & vbcrlf & _ "INSERT INTO [Order Details] " & vbcrlf & _ " (OrderID, ProductID, Quantity, UnitPrice) " & vbcrlf & _ " @UnitPrice)" cmd.commandtext = strsql cmd.executenonquery( ) strsql = "CREATE PROCEDURE spdeletedetail " & vbcrlf & _ " (@OrderID int, " & vbcrlf & _ money) " & vbcrlf & _ "AS " & vbcrlf & _ "DELETE FROM [Order Details] " & vbcrlf & _ " WHERE OrderID AND " & vbcrlf & _ " ProductID AND " & vbcrlf & _ " Quantity AND UnitPrice cmd.commandtext = strsql cmd.executenonquery( ) End Sub C# static void SubmitChangesViaStoredProcedures( ) da.updatecommand = CreateUpdateViaSPCommand( ); da.insertcommand = CreateInsertViaSPCommand( ); da.deletecommand = CreateDeleteViaSPCommand( ); da.update(tbl); static OleDbCommand CreateUpdateViaSPCommand( ) OleDbCommand cmd = new OleDbCommand("spUpdateDetail", cn); cmd.commandtype = CommandType.StoredProcedure; -22-
23 OleDbParameterCollection pc = cmd.parameters; pc.add("orderid_new", OleDbType.Integer, 0, "OrderID"); pc.add("productid_new", OleDbType.Integer, 0, "ProductID"); pc.add("quantity_new", OleDbType.SmallInt, 0, "Quantity"); pc.add("unitprice_new", OleDbType.Currency, 0, "UnitPrice"); OleDbParameter param; param = pc.add("orderid_orig", OleDbType.Integer, 0, "OrderID"); param.sourceversion = DataRowVersion.Original; param = pc.add("productid_orig", OleDbType.Integer, 0, "ProductID"); param.sourceversion = DataRowVersion.Original; param = pc.add("quantity_orig", OleDbType.SmallInt, 0, "Quantity"); param.sourceversion = DataRowVersion.Original; param = pc.add("unitprice_orig", OleDbType.Currency, 0, "UnitPrice"); param.sourceversion = DataRowVersion.Original; return cmd; static OleDbCommand CreateInsertViaSPCommand( ) OleDbCommand cmd = new OleDbCommand("spInsertDetail", cn); cmd.commandtype = CommandType.StoredProcedure; OleDbParameterCollection pc = cmd.parameters; pc.add("orderid", OleDbType.Integer, 0, "OrderID"); pc.add("productid", OleDbType.Integer, 0, "ProductID"); pc.add("quantity", OleDbType.SmallInt, 0, "Quantity"); pc.add("unitprice", OleDbType.Currency, 0, "UnitPrice"); return cmd; static OleDbCommand CreateDeleteViaSPCommand( ) OleDbCommand cmd = new OleDbCommand("spDeleteDetail", cn); cmd.commandtype = CommandType.StoredProcedure; OleDbParameterCollection pc = cmd.parameters; OleDbParameter param; param = pc.add("orderid", OleDbType.Integer, 0, "OrderID"); param.sourceversion = DataRowVersion.Original; param = pc.add("productid", OleDbType.Integer, 0, "ProductID"); param.sourceversion = DataRowVersion.Original; param = pc.add("quantity", OleDbType.SmallInt, 0, "Quantity"); param.sourceversion = DataRowVersion.Original; param = pc.add("unitprice", OleDbType.Currency, 0, "UnitPrice"); param.sourceversion = DataRowVersion.Original; return cmd; static void CreateSprocs( ) OleDbCommand cmd = cn.createcommand( ); string strsql; strsql = "CREATE PROCEDURE spupdatedetail n r" + " (@OrderID_New int, n r" + money, n r"
24 int, n r" + money) n r" + "AS n r" + "UPDATE [Order Details] n r" + " SET OrderID n r" + " ProductID n r" + " Quantity n r" + " UnitPrice n r" + " WHERE OrderID AND n r" + " ProductID AND n r" + " Quantity AND n r" + " UnitPrice cmd.commandtext = strsql; cmd.executenonquery( ); strsql = "CREATE PROCEDURE spinsertdetail n r" + " (@OrderID int, n r" + money) n r" + "AS n r" + "INSERT INTO [Order Details] n r" + " (OrderID, ProductID, Quantity, UnitPrice) n r" + " @UnitPrice)"; cmd.commandtext = strsql; cmd.executenonquery( ); strsql = "CREATE PROCEDURE spdeletedetail n r" + " (@OrderID int, n r" + money) n r" + "AS n r" + "DELETE FROM [Order Details] n r" + " WHERE OrderID AND n r" + " ProductID AND n r" + " Quantity AND UnitPrice cmd.commandtext = strsql; cmd.executenonquery( ); 独自の更新ロジックの作成 其れでは此処で コードの中に自分で更新ロジックを用意する利点と欠点を纏めて置く 利点 自分で更新ロジックを用意する最大の利点は 制御とパフォーマンスで有る ADO.NET の DataAdapter では Microsoft の従来のデータアクセステクノロジよりも 更新ロジックを制御出来る余地が大きく成って居る テーブルに対して直に更新を適用する他に スマートな方法でストアドプロシージャを活用出来る様にも成った 更に データアクセステクノロジに頼らずにデータの出所を判別出来る為 何んな結果セットでも更新用に利用出来る ADO カーソルエンジンの場合は データベースの更新に必要なメタデータをコードの中で用意する事が出来なかった 詰まり カーソルエンジンが入手する情報が総てだった訳で有る 処が ADO.NET では ストアドプロシージャから返される結果 一時テーブルへのクエリから返される結果 結合クエリから返される結果等 何んな手段で用意したデータで有っても DataSet のデータと仕て利用出来るし 其のデータの変更内容をデータベースに適用出来る -24-
25 更に コードの中に自分で更新ロジックを用意すると アプリケーションのパフォーマンスが向上する ADO カーソルエンジンを使ってデータベースを更新するコードは コーディングの量自体は少なくて済むが 其の一方で ADO カーソルエンジンが ソーステーブル名 ソース列名 ソーステーブルの主キー情報をデータベースクエリに依って抽出する必要が有る データベースシステムのテーブルからメタデータを取得し 其のメタデータに基づいて更新ロジックを生成する方法は 単に更新ロジックをローカルコードから読み込む方法に比べて時間が懸かる 欠点 自分で更新ロジックを用意する欠点は ADO カーソルエンジンを使用するメリットの裏返しでも有る 先ず 自分で更新ロジックを用意する為にコードの量が多く成る 少し振り返って ADO.NET の DataAdapter を使ってデータベースを更新するコードと ADO カーソルエンジンを使ったコードを比べて観て欲しい ADO.NET の場合は コードを書くのに時間が懸かり過ぎて嫌気が差すかも知れない 今 1 つのデメリットは 多くの開発者が更新ロジックのコーディングに慣れて居ないと謂う事で有る クエリの中でテーブル名を区切り文字で囲む必要が有るだろうか 何んなパラメータマーカーを使ったら良いだろうか UpdateCommand と DeleteCommand の CommandText の WHERE 句には何んな列を指定する可きだろうか 日付時刻値のパラメータでは OleDbType プロパティを何う設定したら良いだろうか 勿論 誰でも然う謂う事で頭を悩ませ度くは無い 処が 更新ロジックを生成する為の更にスマートな方法が有る 次に 其の方法を取り上げる 更新ロジックを生成する為の CommandBuilder オブジェクトの使用 ADO.NET のオブジェクトモデルでは 開発者が自分で更新ロジックを定義出来る許りか ADO カーソルエンジンに良く似た動的な更新ロジック生成機能が用意されて居る 其れが CommandBuilder オブジェクトで有る CommandBuilder オブジェクトのインスタンスを作成し 其のインスタンスを DataAdapter オブジェクトに関連付ければ 其の CommandBuilder は DataAdapter オブジェクトの SelectCommand に指定されて居るクエリに基づいて更新ロジックを生成する 其れでは CommandBuilder の働きを観る為に CommandBuilder を使って実際に更新ロジックを生成して観る事にする Order Details テーブルを更新する先程のコードで使った更新ロジックで有る 次に示すのは コンストラクタに OleDbDataAdapter を指定して OleDbCommandBuilder のインスタンスを作成し 新しい行をデータベースに送信する為に CommandBuilder が生成した Command のテキストを書き出すコードで有る Visual Basic Dim strconn, strsql As String strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" & _ "Initial Catalog=Northwind;Trusted_Connection=Yes;" strsql = "SELECT OrderID, ProductID, Quantity, UnitPrice " & _ "FROM [Order Details] WHERE OrderID = " & _ "ORDER BY ProductID" Dim da As New OleDbDataAdapter(strSQL, strconn) Dim cb As New OleDbCommandBuilder(da) Console.WriteLine(cb.GetInsertCommand.CommandText) C# string strconn, strsql; strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" + "Initial Catalog=Northwind;Trusted_Connection=Yes;"; -25-
26 strsql = "SELECT OrderID, ProductID, Quantity, UnitPrice " + "FROM [Order Details] WHERE OrderID = " + "ORDER BY ProductID"; OleDbDataAdapter da = new OleDbDataAdapter(strSQL, strconn); OleDbCommandBuilder cb = new OleDbCommandBuilder(da); Console.WriteLine(cb.GetInsertCommand( ).CommandText); 此のクエリのテキストは 新しい行をデータベースに送信する為に先程手作業で作成したクエリに驚く程良く似て居る INSERT INTO Order Details( OrderID, ProductID, Quantity, UnitPrice ) VALUES (?,?,?,? ) CommandBuilder オブジェクトが更新ロジックを生成する方法 CommandBuilder が UPDATE INSERT DELETE の各クエリを生成する為のロジックは 其れ程複雑では無い ADO カーソルエンジンの場合と同じ様に CommandBuilder も データベースクエリに依って 基本テーブルと列の名前とクエリ結果のキー情報を取り込む CommandBuilder に依って更新ロジックを生成出来るのは 次の総ての条件が満たされて居る場合で有る クエリが只 1 つのテーブルからデータを返す場合 其のテーブルに主キーが有る場合 其の主キーがクエリ結果に含まれて居る場合 既に触れた通り 主キーが存在すれば クエリ結果に基づいて CommandBuilder が生成する更新ロジックで 1 つの行しか更新されない事に成る 其れでは CommandBuilder で クエリの対象と成るテーブルの数に制限が有るのは何故だろうか 其の点に付いては 後述する CommandBuilder オブジェクトは DataAdapter オブジェクトの SelectCommand に依って 更新ロジックに必要なメタデータを取得する Command オブジェクトの ExecuteReader を使用すれば クエリ結果と一緒に其の様なメタデータを抽出出来る 次に示すのは 其の様なコードの一例で有る Visual Basic Dim strconn, strsql As String strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" & _ "Initial Catalog=Northwind;Trusted_Connection=Yes;" strsql = "SELECT OrderID, ProductID, Quantity, UnitPrice " & _ "FROM [Order Details] WHERE OrderID = " & _ "ORDER BY ProductID" Dim cn As New OleDbConnection(strConn) Dim cmd As New OleDbCommand(strSQL, cn) cn.open( ) Dim rdr As OleDbDataReader rdr = cmd.executereader(commandbehavior.schemaonly Or CommandBehavior.KeyInfo) Dim tbl As DataTable = rdr.getschematable rdr.close( ) cn.close( ) Dim row As DataRow Dim col As DataColumn For Each row In tbl.rows For Each col In tbl.columns Console.WriteLine(col.ColumnName & ": " & row(col).tostring) Next col Console.WriteLine( ) Next row -26-
27 C# string strconn, strsql; strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" + "Initial Catalog=Northwind;Trusted_Connection=Yes;"; strsql = "SELECT OrderID, ProductID, Quantity, UnitPrice " + "FROM [Order Details] WHERE OrderID = " + "ORDER BY ProductID"; OleDbConnection cn = new OleDbConnection(strConn); OleDbCommand cmd = new OleDbCommand(strSQL, cn); cn.open( ); OleDbDataReader rdr; rdr = cmd.executereader(commandbehavior.schemaonly CommandBehavior.KeyInfo); DataTable tbl = rdr.getschematable( ); rdr.close( ); cn.close( ); foreach (DataRow row in tbl.rows) foreach (DataColumn col in tbl.columns) Console.WriteLine(col.ColumnName + ": " + row[col].tostring( )); Console.WriteLine( ); 此のコードを実行すると CommandBuilder が更新ロジックを生成する為に必要とする各列のデータが総て書き出される 詰まり 列の名前は何か 其の列の基本テーブルと基本列の名前は何か 其の列は基本テーブルの主キーに成って居るか 其の列の値は long データ型 ( ラージテキストやバイナリ ) なのか 浮動小数点列の有効桁数と小数部桁数は何かと謂ったデータで有る CommandBuilder を使用する利点と欠点 CommandBuilder を使ったコードと 自分で更新ロジックを用意する為に使用したコードを比べて観ると CommandBuilder オブジェクトを使用する 2 つの利点が浮かび上がって来る 1 つは CommandBuilder オブジェクトを使えばコードの量が少なくて済むと謂う事 然して今 1 つは UPDATE INSERT DELETE の各クエリの SQL 構文に付いて余り知らなくても更新ロジックを生成出来ると謂う事で有る 更に CommandBuilder は 自分で更新ロジックを生成しようと仕て問題に打つかった時にも役立つ CommandBuilder で旨く更新ロジックを生成出来れば Command オブジェクトの CommandText プロパティの値や Parameter オブジェクトの様々なプロパティを参考に出来る 亦 CommandBuilder は 更新をサポートしなければ成らないのに デザイン時にはクエリの構造が解らないと謂った場合にも非常に便利で有る 一方 ADO カーソルエンジンと同じ様に CommandBuilder も実行時に自動的に更新ロジックを生成するので ADO カーソルエンジンと同じ問題や制限が有る CommandBuilder は 実行時のパフォーマンスが余り高く無い コードの中に自分で更新ロジックを用意した場合に比べて CommandBuilder が更新ロジックの生成に必要なメタデータを抽出して処理するには時間が懸かる 亦 CommandBuilder では 更新ロジックの生成を制御する余地が無い オプティミスティックな同時実行制御の種類を指定する事も出来ない 更に CommandBuilder は ストアドプロシージャに依る更新にも対応して居ない -27-
28 其れでは デザイン時に簡単に更新ロジックを生成する手段は無いのだろうか 更新ロジックを生成する為のデータアダプタ構成ウィザードの使用 データアダプタ構成ウィザードは OLE DB/SQL Server.NET データプロバイダを使用して DataAdapter オブジェクトを作成する為の機能を備えて居るが 実は 更新ロジックを生成してコードの中に格納する時にも 此のウィザードを使用出来る データアダプタ構成ウィザードの 1 つの目的は デザイン時に更新ロジックを自動生成して 効率的な更新コードを簡単に作成する事で有る 確かに野心的な目標では有るが 此のウィザードは其の点で殆んどの状況に対応出来る ( 尤も ウィザード に仕ては使い方が難しいと謂う印象も有るが ) 先ず デザイナが用意されて居るプロジェクトアイテム (Windows フォーム Web フォーム Web サービス コンポーネント等 ) を含んだプロジェクトを Visual Studio.NET で開き 其のデザイナに OleDbDataAdapter を追加し Northwind データベースへの接続を指定してから 此のウィザードの SQL ステートメントのページで次のクエリを入力する SELECT OrderID, ProductID, UnitPrice, Quantity FROM [Order Details] WHERE OrderID =? ORDER BY ProductID 其の後 [ 次へ ] をクリックすると [ ウィザードの結果の表示 ] 画面が表示され 図 10.3 の様な結果が出力される 図 10.3 データアダプタ構成ウィザードの [ ウィザードの結果の表示 ] 画面 -28-
29 DataAdapter の構造の調査 図 10.3 から解る通り 此のウィザードは 新しい DataAdapter の UPDATE INSERT DELETE の各クエリを生成した [ 完了 ] ボタンをクリックして此のページを閉じてから コンポーネントトレイで新しい DataAdapter を選択し [ プロパティ ] ウィンドウを表示して DataAdapter オブジェクトの DeleteCommand を探す CommandText プロパティを選択してから 其のプロパティの値の右側に有るボタンをクリックすると クエリビルダが表示され DataAdapter オブジェクトの DeleteCommand の CommandText が書き出される ( 図 10.4 を参照 ) 図 10.4 ウィザードに依って生成された DeleteCommand 此の画面から解る通り 保留状態の削除操作を実行する為にデータアダプタ構成ウィザードが生成したクエリは 先程手作業で作成したクエリと同じで有る 更に DataAdapter オブジェクトの InsertCommand と UpdateCommand に移動して ウィザードが生成した更新ロジックの残りの部分を確認して観て欲しい 更新ロジックの構築の選択肢 此のウィザードの [SQL ステートメントの生成 ] 画面には [ 詳細オプション ] ボタンが有る 此のボタンをクリックすると 一連のオプションを含んだダイアログボックスが表示される ( 図 10.5 を参照 ) 此等のオプションに依って データアダプタ構成ウィザードから生成される更新ロジックの細かい点を制御する事が出来る データベースからデータを取り出す為丈に DataAdapter を使用する場合は [INSERT UPDATE 及び DELETE ステートメントの生成 ] オプションをオフにして デザイン時にも実行時にも少しの時間を節約出来る -29-
30 既定の設定では 保留状態の更新や削除を実行するクエリの WHERE 句に BLOB 以外の総ての列が追加されるが [ オプティミスティック同時実行制御 ] オプションをオフにすると 其の WHERE 句に主キー列丈が組み込まれる 図 10.5 データアダプタ構成ウィザードの [SQL 生成の詳細オプション ] 画面 SQL Server 等 一部のデータベースでは データ行を返すバッチクエリがサポートされて居る 其の様なデータベースと通信する DataAdapter を作成する場合は [ データセットの更新 ] オプションが表示される ( 既定でオンに成って居る ) 此のオプションをオンの儘に仕て置くと データベースを更新した直後に変更行の内容を再び取り出す為のクエリが生成される 詰まり DataAdapter.Update の呼出後の DataSet には timestamp 値や自動インクリメント値等 新しいサーバー生成値が取り込まれると謂う事で有る 更新を実行する為のストアドプロシージャの使用 データアダプタ構成ウィザードは ストアドプロシージャを使って SQL Server データベースを更新する DataAdapter オブジェクトを作成する場合にも役立つ 此のウィザードの [ クエリの種類の選択 ] 画面には [ 既存のストアドプロシージャを使用 ] オプションが表示される ( 図 10.6 を参照 ) 其のオプションを選択してから [ 次へ ] をクリックする 図 10.6 データアダプタ構成ウィザードの [ クエリの種類の選択 ] 画面 -30-
31 次の画面では DataAdapter の各 Command オブジェクトのストアドプロシージャを選択する 最初に選択するのは DataAdapter オブジェクトの SelectCommand のストアドプロシージャで有る 図 10.7 に有る様に 選択可能なストアドプロシージャがドロップダウンリストボックスに表示される ストアドプロシージャを選択すると 其のストアドプロシージャから返される列がリストの右側に表示される 図 10.7 DataAdapter オブジェクトの SelectCommand のストアドプロシージャを選択する DataAdapter オブジェクトの SelectCommand の設定が終われば 更新用の Command オブジェクトのストアドプロシージャを指定する 更新用のストアドプロシージャのパラメータに SourceColumn プロパティを設定するには ウィザードの右側のドロップダウンリストを使用する ( 図 10.8 を参照 ) 図 10.8 InsertCommand のパラメータに SourceColumn プロパティを設定する -31-
32 データアダプタ構成ウィザードでは Parameter オブジェクトの SourceVersion プロパティを設定出来ない 其のプロパティの既定値は Current なので パラメータを変更列の元の値にバインドする場合は [ プロパティ ] ウィンドウで パラメータの SourceVersion プロパティの値を変更する必要が有る Visual Studio.NET の Enterprise Edition の場合は DataAdapter オブジェクトの SelectCommand UpdateCommand InsertCommand DeleteCommand の新しい SQL Server ストアドプロシージャを生成する為に SQL クエリを指定する事も出来る データアダプタ構成ウィザードの [ クエリの種類の選択 ] 画面で [ 新しいストアドプロシージャの作成 ] オプションを選択すると データベースのデータを返す SQL クエリを入力する為の画面が表示される ( 図 10.9 を参照 ) 図 10.9 新しいストアドプロシージャの SQL クエリを指定する 次の画面では ウィザードから生成されるストアドプロシージャの名前を指定する 亦 此の画面の [SQL スクリプトのプレビュー ] ボタンをクリックすると ストアドプロシージャの作成用と仕て生成された SQL スクリプトを書き出したダイアログボックスが表示される ( 図 を参照 ) サンプルデータベースを対象と仕たアプリケーションを作成して居た場合は 此のダイアログボックスから SQL スクリプトをファイルに保存し 後から実稼働データベースに対して其のスクリプトを実行すると謂う使い方が出来る 図 新しいストアドプロシージャを作成する為の SQL スクリプトを表示した画面 -32-
33 此のウィザードが完了すると データベース内に新しいストアドプロシージャが作成され 新しい DataAdapter が其のストアドプロシージャを使用する様に設定される ウィザードを使用する利点と欠点 既に触れた通り データアダプタ構成ウィザードを使用する目的の 1 つは 効率的な更新コードを簡単に作成する為の更新ロジックを生成する事で有る 此のウィザードでは CommandBuilder オブジェクトの場合よりも多くのオプションを指定出来る 亦 開発者が先ず書かない様な長いコードも生成して下れる 此のウィザードも CommandBuilder オブジェクトと同じ様に 更新ロジックを生成する為にスキーマ情報をデータベースから取得するが 此のウィザードの場合は スキーマ情報をデザイン時に一度丈取得した後 其の情報に基づいて新しく生成したロジックをコードの中に保存する 従って CommandBuilder オブジェクトの様に実行時にパフォーマンスが下がると謂う事は無い 併し データアダプタ構成ウィザードも完全では無い Visual Studio.NET の最初のリリースでは OLE DB/SQL Server.NET データプロバイダの DataAdapter オブジェクト丈が 此のウィザードの対象に成って居る 亦 同時実行制御のオプションも限られて居る 此のウィザードが生成した更新用の Command オブジェクトに手を加える事は可能だが 其の様に仕て加えた変更は DataAdapter を再構成した時に失われて仕舞う 併し 此の様な制限が有るとは謂え 此のウィザードは矢張り非常に便利で強力なツールで有る事に違いは無い 更新に関する其の他の問題 此れで DataSet 内に格納されて居る変更内容に基づいてデータベースを更新する基本的な方法が解った 併し 自分で更新ロジックを用意する場合は (INSERT UPDATE DELETE の各クエリと仕て用意すると仕ても ストアドプロシージャ呼出の形で用意すると仕ても ) 基本的な事に加えて更に幾つかの点を理解して置く必要が有る 例えば 別のユーザーが加えた変更を間違って上書きして仕舞わない様にする為の同時実行制御は 何の様に設定したら良いだろうか 同時実行制御のチェックで NULL 値は何う処理するのだろうか トランザクションの中で更新を実行するには 何うしたら良いのだろうか データベースに更新を適用する時に DataAdapter の TableMappings コレクションは何んな役割を果たすのだろうか 其れでは 斯うした点を 1 つ宛観て行く事にする オプティミスティックな同時実行オプション マルチユーザーのデータベースアプリケーションで データベースの更新時にオプティミスティックな同時実行制御を活用する場合は 更新クエリの中でオプティミスティックな同時実行制御のチェックを正しく実行する事が重要に成る 例えば 其の様なアプリケーションで 2 人のユーザーが同じデータ行を要求し 同じデータ行を更新しようと仕たとする 何う謂う事に成るだろうか 其の結果は 更新クエリの作成方法に依って決まる SQL 更新クエリのオプティミスティックな同時実行制御には 4 つの基本的なオプションが有る 主キー列丈を含めるオプション -33-
34 此れは SQL の UPDATE クエリと DELETE クエリに主キー丈を含めると謂うオプションで有る 此の場合の更新は 最新操作の優先 と謂う形に成る 孰れの更新操作も成功するが データベースに両方の更新内容が共存する事は不可能なので 結果的には片方丈が残る 詰まり 後から適用された更新に依って 其れ以前の更新が上書きされると謂う事で有る 此の流れを纏めると 次の様に成る ユーザー A が行を取り出す ユーザー B が其れと同じ行を取り出す ユーザー B が其の行を変更し 変更内容をデータベースに適用する ( 更新は成功する ) ユーザー A が其の行を変更し 変更内容をデータベースに適用する ( 更新は成功し ユーザー B の変更内容を上書きする ) ユーザー A は 最初のクエリで行を取得してから データベースに変更内容を適用する迄の間に 別のユーザーに依ってデータベースの内容が変更された事に気付きもしない 最新操作の優先 が望ましい場合は 此のオプションを選択出来る 併し ユーザー同士の間で無意識に依る上書きを防止したければ 此のオプションでは不都合で有る オプティミスティック同時実行制御の此のオプションは CommandBuilder オブジェクトには用意されて居ないが データアダプタ構成ウィザードからは利用出来る 其の為には [ 詳細オプション ] ページで [ オプティミスティック同時実行制御 ] チェックボックスをオフにする WHERE 句に総ての列を含めるオプション 其れでは 最新操作の優先 に依る更新では都合が悪い場合は 何うすれば良いだろうか 詰まり ユーザー A がデータベースクエリで行を取得してからの行の変更内容をデータベースに適用する迄の間にデータベースに対して加えられた変更を ユーザー A が上書きしない様にすると謂うケースで有る CommandBuilder もデータアダプタ構成ウィザードも WHERE 句に総ての列を含めると謂うのが既定の動作に成って居る 此のロジックを使用すれば 行を取り出してから保留状態の変更内容をデータベースに反映させる迄の間に他のユーザーが適用した変更内容が上書きされなく成る 例えば ユーザー A とユーザー B が顧客データの同じ行を取り出すとする ユーザー B が ContactName 列に変更を加えて 其の変更内容をデータベースに適用する場合 アプリケーションは UPDATE クエリの WHERE 句に総ての列を含めるので UPDATE クエリは次の様に成る UPDATE Customers SET CustomerID = 'ABCDE', CompanyName = 'Original Company Name', ContactName = 'New Contact', Phone = ' ' WHERE CustomerID = 'ABCDE' AND CompanyName = 'Original Company Name' AND ContactName = 'Original Contact' AND Phone = ' ' 処が 其の間に ユーザー A も顧客データの其の同じ行を取り込み CompanyName 列の値を変更する ユーザー A が其の行を取り出したのは ユーザー B が ContactName 列への変更内容をデータベースに適用する前だったので ユーザー A の UPDATE クエリは次の様に成る -34-
35 UPDATE Customers SET CustomerID = 'ABCDE', CompanyName = 'New Company Name', ContactName = 'Original Contact', Phone = ' ' WHERE CustomerID = 'ABCDE' AND CompanyName = 'Original Company Name' AND ContactName = 'Original Contact' AND Phone = ' ' 此の場合 ユーザー A がデータベースを更新しようとした時点では データベース内の此のデータ行の ContactName 列の値が既にユーザー B に依って変更されて居る為 此のクエリの WHERE 句の基準に該当する行はテーブルの中にもう存在しない 従って データベース内の其の顧客データ行の更新は出来ないと謂う事に成る DataAdapter は 其のクエリに依って更新された行の数を確認する為に更に別のクエリを実行し 其の結果から目的の行が実際には更新されなかった事を判別し 其の結果に合わせて DataRow の状態を設定する と謂う流れに成る 此れは CommandBuilder オブジェクトで使用される同時実行制御オプションで有り データアダプタ構成ウィザードの既定の設定でも有る 基本的に データベースでは BLOB 値同士の比較が出来ない BLOB 列にはメガバイト単位の巨大なデータが格納される事も有る為 BLOB 列の比較操作は ( 不可能では無いに仕ても ) 極度に効率が落ちて仕舞う CommandBuilder やデータアダプタ構成ウィザード等のコード生成ツールでは 更新用クエリの WHERE 句に BLOB 列は含まれない 開発者が自分で更新ロジックを用意する場合も 此の点を押さえて置く必要が有る 主キー列と timestamp 列を含めるオプション timestamp 列を活用すると 更新クエリの WHERE 句が簡単に成る SQL Server の timestamp 列の値は 実際には日付 / 時刻の情報ではなく データベース内で一意のバイナリデータで有る SQL Server のテーブルに timestamp 列を定義すると 行の内容に変更が加えられる度に 其の行の timestamp 列の値が変更される事に成る Customers テーブルに timestamp 列を追加して置けば 先程のクエリを次の様に変更出来る UPDATE Customers SET CustomerID = 'ABCDE', CompanyName = 'Original Company Name', ContactName = 'New Contact', Phone = ' ' WHERE CustomerID = 'ABCDE' AND TimestampColumn = 0x CC 行が更新される度に timestamp 列の値が新しく生成されるので 更新クエリの WHERE 句に主キー列と timestamp 列を組み合わせて指定すれば 他のユーザーに依る変更の上書きを防止出来る 殆どのデータベースでは 此れと似た様なデータ型がサポートされて居る 一意のバイナリ値を使用するデータベースも有れば 日付 / 時刻の値を使用するデータベースも有る バックエンドデータベースのデータ型の詳細や 行の内容が変更される度に其の種の値が更新される様にデータベースを設定する方法に付いては 夫々れのデータベースシステムの資料を参照され度い オプティミスティック同時実行制御の此のオプションを使って更新ロジックを生成する機能は 今の処 CommandBuilder でもデータアダプタ構成ウィザードでもサポートされて居ない -35-
36 SQL Server 2000 から データ型の名称と仕て rowversion と timestamp が同義語に成った SQL Server の資料では キーワードと仕て timestamp よりも rowversion を使う方が望ましいとされて居るが 本稿では 現時点で広く認知されて居る timestamp の方を採用して居る 個人的には 此の様にして同時実行制御のチェックに主キー列と timestamp 列を組み合わせる方法を愛用して居る 此の方法は 更新ロジックがシンプルで有り 1 回の更新処理で照合する列の数が少なくて済むからで有る 主キー列と変更列を含めるオプション ADO カーソルエンジンの場合は 更新クエリの WHERE 句に主キー列と変更列の元の値丈を含めると謂うのが既定の動作に成って居る 亦 UPDATE クエリの SET 句にも 変更列丈を含める様に成って居る 其れでは 先程のマルチユーザーアプリケーションで 此の更新オプションを使うと何う成るだろうか 例えば ユーザー A とユーザー B が同じ顧客データ行を同時に取り出したとする 2 人のユーザーは夫々れ違う列を変更する ユーザー A は CompanyName 列 ユーザー B は ContactName 列で有る 此の状態で 先ずユーザー B が ContactName 列の変更をデータベースに反映させる ユーザー B の UPDATE クエリは 次の通りで有る UPDATE Customers SET ContactName = 'New Contact' WHERE CustomerID = 'ABCDE' AND ContactName = 'Original Contact' 其の後 ユーザー A が CompanyName 列の保留状態の変更内容をデータベースに適用する ユーザー A の UPDATE クエリは 次の通りで有る UPDATE Customers SET CompanyName = 'New Company Name' WHERE CustomerID = 'ABCDE' AND CompanyName = 'Original Company Name' 最初 行は次の様な内容で有る CustomerID CompanyName ContactName ABCDE Original Company Name Original Contact 先ずユーザー B に依って次の様に変更される CustomerID CompanyName ContactName ABCDE Original Company Name New Contact 更にユーザー A に依って次の様に変更される CustomerID CompanyName ContactName ABCDE New Company Name New Contact -36-
37 此の様に 孰れの更新操作も成功し ユーザー B に依る変更はユーザー A に依る変更に依って上書きされない 処が ADO.NET の DataAdapter の構造は 此のオプションに依る更新に適して居ない 此のオプションでは 保留状態の変更内容が含まれる行の変更列に基づいてクエリの構造を修正する必要が有るが DataAdapter は 更新クエリのパラメータ値を行単位で渡す丈で パラメータクエリの実際の構造は変更しないからで有る 理論的には 該当する Command オブジェクトの構造を動的に変更するコードを作成し DataAdapter オブジェクトの RowUpdating イベントを処理する時に其のコードを使用すると謂う方法も可能で有る 確かに 此の更新オプションにも其れなりの利点が有るが 矢張り欠点の方が大きいと思う NULL 値の処理 Northwind データベースの Customers テーブルに含まれて居る Region 列には 15 文字以下の文字列又は NULL が入る 現に Region 列の値が NULL に成って居る行は可成り有る 其の種の行を取り出す為のクエリと仕て 次の様なクエリが思い浮かぶかも知れない SELECT CustomerID, CompanyName, ContactName, Phone FROM Customers WHERE Region = NULL 処が 此のクエリを ADO.NET で使用しても SQL クエリアナライザで実行しても 返される行数はゼロで有る 詰まり 1 行も返されない データベースの世界で NULL は特殊な値で有り 特にクエリの中で NULL 値の比較を実行する場合は特殊な動作をする ANSI 規格では = 演算子に依る比較の対象に NULL 値を指定する事は出来ない 寧ろ 其の種のクエリでは IS NULL を使用する必要が有る Customers テーブルで Region 列の値が NULL に成って居る行を取り込むクエリは 次の通りで有る SELECT CustomerID, CompanyName, ContactName, Phone FROM Customers WHERE Region IS NULL 其れに仕ても DataAdapter を使ってデータベースを更新する処理と 此の NULL 値は何んな関係が有るのだろうか 一例と仕て Order Details テーブルの行の変更内容をデータベースに適用する為に作成した先程の Command オブジェクトの CommandText を観てみる事にする UPDATE [Order Details] SET OrderID =?, ProductID =?, Quantity =?, UnitPrice =? WHERE OrderID =? AND ProductID =? AND Quantity =? AND UnitPrice =? 此のクエリで参照して居る列は 孰れも NULL 値を受け入れないので 此のクエリの WHERE 句は比較的シンプルで有る 併し 仮に Quantity 列と UnitPrice 列が NULL 値を受け入れるとすれば 何う成るだろうか 例えば Quantity 列の値が NULL に成って居る行の其の NULL 値を 20 に変更する場合 パラメータを実際の値に書き換えると クエリは次の様に成る UPDATE [Order Details] SET OrderID = 12345, ProductID = 1, Quantity = 20, UnitPrice = 18 WHERE OrderID = AND ProductID = 1 AND Quantity = Null AND UnitPrice =
38 此の場合は 何の行も変更されない 問題は WHERE 句の Quantity = Null と謂う部分で有る データベース内の目的の行の Quantity 列は NULL だが Null = Null は false に成って仕舞う為 其の行は更新されない 其れでは 同時実行制御のチェックで NULL 値を正しく指定するには クエリの WHERE 句を何う変更すれば良いのだろうか NULL を受け入れる列の場合 其の列を指定する時に クエリの次の部分を変更する ColumnName =? 変更後の指定は 次の様に成る (ColumnName =? OR ((ColumnName IS NULL) AND (? IS NULL))) 此れで 列の値とパラメータの値が同じ非 NULL 値の場合や 列の値とパラメータの値が両方共 NULL の場合に 此の WHERE 句が true に成る 例えば DataAdapter から Customers テーブルの CustomerID CompanyName ContactName Phone の各列を対象としたクエリを実行するとする CustomerID 列と CompanyName 列は NULL を受け入れないが ContactName 列と Phone 列は NULL を受け入れるので 更新クエリの WHERE 句で NULL チェックを正しく設定する必要が有る データアダプタ構成ウィザードを使って更新ロジックを作成すると 変更行をデータベースに適用する次の様なクエリが生成される 此のクエリでは 正しい NULL チェックが設定されて居る UPDATE Customers SET CustomerID =?, CompanyName =?, ContactName =?, Phone =? WHERE (CustomerID =?) AND (CompanyName =?) AND (ContactName =? OR? IS NULL AND ContactName IS NULL) AND (Phone =? OR? IS NULL AND Phone IS NULL) 既に確認したが データアダプタ構成ウィザードが生成する更新ロジックは非常に良く出来て居る 開発者が自分で更新ロジックを用意する場合も 此のウィザードが生成するコードを参考にすれば コードのダブルチェックが出来る トランザクション内での更新の実行 総ての更新操作を 1 つの作業単位の中で実行し 総てが成功するか 総てが失敗するかの孰れかに仕度い場合は 何うすれば良いだろうか 答えは簡単で有る 総ての更新を 1 つのトランザクションの中に組み込むと謂う事で有る 併し DataAdapter には Transaction プロパティが用意されて居ない DataAdapter は 実際に更新を実行する訳では無い 寧ろ 実際の更新操作は Command オブジェクトに任せて仕舞う ( 其れ故に DataAdapter には UpdateCommand InsertCommand DeleteCommand の各プロパティが用意されて居る ) 其の Command オブジェクトには Transaction プロパティが用意されて居るので DataAdapter を使ってデータベースに対する更新をトランザクションと仕て実行するには DataAdapter が使用する Command オブジェクトの Transaction プロパティを設定すれば良い訳で有る -38-
39 次に 其の様なコードの一例を示す Visual Basic Dim strconn, strsql As String strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" & _ "Initial Catalog=Northwind;Trusted_Connection=Yes;" strsql = "SELECT OrderID, ProductID, Quantity, UnitPrice " & _ "FROM [Order Details] WHERE OrderID = " & _ "ORDER BY ProductID" Dim tbl As New DataTable( ) Dim cn As New OleDbConnection(strConn) Dim da As New OleDbDataAdapter(strSQL, cn) ' DataAdapter の更新ロジックを定義する ' 接続を開いて クエリの結果を取り込む cn.open( ) da.fill(tbl) ' DataTable の内容を変更する ' 新しいトランザクションを作成する Dim txn As OleDbTransaction = cn.begintransaction( ) ' DataAdapter の各 Command の Transaction プロパティを設定する da.updatecommand.transaction = txn da.insertcommand.transaction = txn da.deletecommand.transaction = txn ' 変更をデータベースに適用する da.update(tbl) ' 変更をコミットし 接続を閉じる txn.commit( ) cn.close( ) C# string strconn, strsql; strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" + "Initial Catalog=Northwind;Trusted_Connection=Yes;"; strsql = "SELECT OrderID, ProductID, Quantity, UnitPrice " + "FROM [Order Details] WHERE OrderID = " + "ORDER BY ProductID"; DataTable tbl = new DataTable( ); OleDbConnection cn = new OleDbConnection(strConn); OleDbDataAdapter da = new OleDbDataAdapter(strSQL, cn); // DataAdapter の更新ロジックを定義する // 接続を開いて クエリの結果を取り込む cn.open( ); da.fill(tbl); // DataTable の内容を変更する // 新しいトランザクションを作成する OleDbTransaction txn = cn.begintransaction( ); // DataAdapter の各 Command の Transaction プロパティを設定する da.updatecommand.transaction = txn; da.insertcommand.transaction = txn; da.deletecommand.transaction = txn; -39-
40 // 変更をデータベースに適用する da.update(tbl); // 変更をコミットし 接続を閉じる txn.commit( ); cn.close( ); トランザクション内でデータベースを更新する処理は CommandBuilder オブジェクトを使って更新ロジックを生成する時には少し難しく成る 更新ロジックが生成されるのは CommandBuilder のインスタンスを作成した時点ではなく DataAdapter オブジェクトの Update メソッドを呼び出した時点で有る CommandBuilder を使ってトランザクション内でデータベースを更新する場合は 此の動作が問題に成る 次の様なコードを使って保留状態の変更内容をデータベースに反映させようとすると ADO.NET から例外が出される Visual Basic Dim strconn, strsql As String... Dim tbl As New DataTable( ) Dim cn As New OleDbConnection(strConn) Dim da As New OleDbDataAdapter(strSQL, cn) Dim cb As New OleDbCommandBuilder(da) cn.open( ) da.fill(tbl) Dim txn As OleDbTransaction = cn.begintransaction( ) da.update(tbl) txn.commit( ) cn.close( ) C# string strconn, strsql;... DataTable tbl = new DataTable( ); OleDbConnection cn = new OleDbConnection(strConn); OleDbDataAdapter da = new OleDbDataAdapter(strSQL, cn); OleDbCommandBuilder cb = new OleDbCommandBuilder(da); cn.open( ); da.fill(tbl); OleDbTransaction txn = cn.begintransaction( ); da.update(tbl); txn.commit( ); cn.close( ); DataAdapter.Update を呼び出すと CommandBuilder は DataAdapter オブジェクトの SelectCommand を使ってデータベースから必要なメタデータを取り込む事に成る 処が SelectCommand プロパティの Command オブジェクトと新しく作成したトランザクションとを関連付ける指定がコードの中に記述されて居ないので CommandBuilder は SelectCommand を使用する事が出来ず CommandBuilder から例外が出される事に成る 此の場合 DataAdapter オブジェクトの Update メソッドの直前に次の 1 行を追加すると コードはエラーに成らない da.selectcommand.transaction = txn -40-
41 処が 此れでは CommandBuilder がデータベースからスキーマ情報を取り込む処理がトランザクション内に組み込まれる事に成って仕舞う 基本的には トランザクション内でのデータベース処理は最小限に押さえた方が良いので トランザクションの開始前に CommandBuilder から更新ロジックを生成すると謂う流れが適切だと謂う事に成る 其の為には CommandBuilder オブジェクトの GetUpdateCommand( 又は GetInsertCommand や GetDeleteCommand) メソッドを呼び出す 其の後で CommandBuilder が生成した Command オブジェクトを新しい Transaction オブジェクトに関連付ければ トランザクション内で DataAdapter に依るデータベース更新丈が実行される事に成る 其の為のコードは 次の通りで有る Visual Basic Dim strconn, strsql As String... Dim tbl As New DataTable( ) Dim cn As New OleDbConnection(strConn) Dim da As New OleDbDataAdapter(strSQL, cn) Dim cb As New OleDbCommandBuilder(da) cn.open( ) cb.getupdatecommand( ) da.fill(tbl) Dim txn As OleDbTransaction = cn.begintransaction( ) cb.getupdatecommand.transaction = txn cb.getinsertcommand.transaction = txn cb.getdeletecommand.transaction = txn da.update(tbl) txn.commit( ) cn.close( ) C# string strconn, strsql;... DataTable tbl = new DataTable( ); OleDbConnection cn = new OleDbConnection(strConn); OleDbDataAdapter da = new OleDbDataAdapter(strSQL, cn); OleDbCommandBuilder cb = new OleDbCommandBuilder(da); cn.open( ); cb.getupdatecommand( ); da.fill(tbl); OleDbTransaction txn = cn.begintransaction( ); cb.getupdatecommand( ).Transaction = txn; cb.getinsertcommand( ).Transaction = txn; cb.getdeletecommand( ).Transaction = txn; da.update(tbl); txn.commit( ); cn.close( ); TableMappings コレクションの使用 DataAdapter オブジェクトの Fill メソッドに依って DataSet にデータを取り込む場合は DataAdapter オブジェクトの TableMappings コレクションに依って其の時の処理を制御出来る 次に示すのは DataAdapter の Fill メソッドを呼び出して 新しい DataTable を作成し 其の TableName プロパティを Table に設定するコードで有る -41-
42 Visual Basic Dim strconn, strsql As String strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" & _ "Initial Catalog=Northwind;Trusted_Connection=Yes;" strsql = "SELECT OrderID, ProductID, Quantity, UnitPrice " & _ "FROM [Order Details] WHERE OrderID = " & _ "ORDER BY ProductID" Dim da As New OleDbDataAdapter(strSQL, strconn) Dim ds As New DataSet( ) da.fill(ds) C# string strconn, strsql; strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" + "Initial Catalog=Northwind;Trusted_Connection=Yes;"; strsql = "SELECT OrderID, ProductID, Quantity, UnitPrice " + "FROM [Order Details] WHERE OrderID = " + "ORDER BY ProductID"; OleDbDataAdapter da = new OleDbDataAdapter(strSQL, strconn); DataSet ds = new DataSet( ); da.fill(ds); 新しい DataTable の TableName を Order Details に設定するには 此のコードを 2 つの方法で変更出来る 1 つは Fill メソッドをオーバーロードして TableName を指定すると謂う方法で有る Visual Basic... Dim da As New OleDbDataAdapter(strSQL, strconn) Dim ds As New DataSet( ) da.fill(ds, "Order Details") Visual C#.NET... OleDbDataAdapter da = new OleDbDataAdapter(strSQL, strconn); DataSet ds = new DataSet( ); da.fill(ds, "Order Details"); 今 1 つは DataAdapter オブジェクトの TableMappings コレクションに 1 つの項目を追加して Order Details の DataTable が対象に成って居る事を DataAdapter に対して指定すると謂う方法で有る Visual Basic... Dim da As New OleDbDataAdapter(strSQL, strconn) da.tablemappings.add("table", "Order Details") Dim ds As New DataSet( ) da.fill(ds) C#... OleDbDataAdapter da = new OleDbDataAdapter(strSQL, strconn); da.tablemappings.add("table", "Order Details"); DataSet ds = new DataSet( ); da.fill(ds); データベースを更新する時にも TableMappings コレクションは 此れと良く似た働きをする DataAdapter オブジェクトの Update メソッドに DataSet オブジェクト丈を指定した場合 DataAdapter は 其の TableMappings コレクションに基づいて DataSet 内の何の DataTable を調べたら良いのかを判別する -42-
43 Visual Basic... Dim da As New OleDbDataAdapter(strSQL, strconn) da.tablemappings.add("table", "Order Details") ' 更新ロジックを定義する Dim ds As New DataSet( ) da.fill(ds) ' 一連の行を変更する da.update(ds) C#... OleDbDataAdapter da = new OleDbDataAdapter(strSQL, strconn); // 更新ロジックを定義する da.tablemappings.add("table", "Order Details"); DataSet ds = new DataSet( ); da.fill(ds); // 一連の行を変更する da.update(ds); DataAdapter オブジェクトの TableMappings コレクションにデータが入って居ない状態で有れば Update メソッドに DataSet とテーブル名の組み合わせを指定するか DataTable オブジェクトを指定する必要が有る Visual Basic... Dim da As New OleDbDataAdapter(strSQL, strconn) ' 更新ロジックを定義する Dim ds As New DataSet( ) da.fill(ds, "Order Details") ' 一連の行を変更する da.update(ds, "Order Details") ' 又は... Dim da As New OleDbDataAdapter(strSQL, strconn) ' 更新ロジックを定義する Dim tbl As New DataTable( ) da.fill(tbl) ' 一連の行を変更する da.update(tbl) C#... OleDbDataAdapter da = new OleDbDataAdapter(strSQL, strconn); // 更新ロジックを定義する DataSet ds = new DataSet( ); da.fill(ds, "Order Details"); // 一連の行を変更する da.update(ds, "Order Details"); // 又は... OleDbDataAdapter da = new OleDbDataAdapter(strSQL, strconn); // 更新ロジックを定義する DataTable tbl = new DataTable( ); da.fill(tbl); // 一連の行を変更する da.update(tbl); -43-
44 此の様に DataTable を指定するロジックは DataAdapter.Fill の場合と DataAdapter.Update の場合とで統一して置くのが原則で有る 最善の更新方法 ADO.NET では データベースを更新する方法が色々用意されて居る 先ず CommandBuilder オブジェクトを使って実行時に更新ロジックを生成すると謂う方法が有る 亦 コードの中に自分で更新ロジックを用意して INSERT UPDATE DELETE の各クエリ 又は ストアドプロシージャに依ってデータベースに変更を適用する事も出来る 更に データアダプタ構成ウィザードを使用すれば デザイン時に其の様なコードを簡単に生成出来る 其れでは 何の方法を使うのが一番良いのだろうか 其の答えは アプリケーションの様々な要素に依って決まる 例えば パフォーマンスが一番高いのは DataAdapter オブジェクトからストアドプロシージャの呼出に依って更新を実行する方法だが ストアドプロシージャをサポートして居ないデータベース (Microsoft Access 等 ) が対象の場合は INSERT UPDATE DELETE の各クエリを使う事に成る 夫々れのアプリケーションで何の方法を採用するかを決めるには 其等の要素を考慮する必要が有る 一般論と仕ては 出来る限りストアドプロシージャに依って変更を実行する事を強く勧める 但し 複数のバックエンドデータベースへの対応を優先する場合は クエリに基づく更新 (INSERT UPDATE DELETE) を使用する 孰れに仕ても 開発者が自分で更新ロジックを用意すると謂う事で有る データアダプタ構成ウィザード等のコード生成ツールを使えば 確かに開発時間を短縮出来る 併し 実行時に更新ロジックを生成すると謂う方法は 最後の手段で有る 本稿で 1 つ丈覚えて置くとすれば 其れは次の点で有る 詰まり 絶対に必要な場合以外は アプリケーションで CommandBuilder オブジェクトを使わないと謂う事で有る 更に高度な更新シナリオも沢山有る 例えば 新しく生成された自動インクリメント値を取り込むには 何うしたら良いのだろうか DataSet から 複数の関連テーブルの行の新規追加 / 削除をデータベースに適用するには 何うすれば良いのだろうか 何うすれば 更新の失敗を検出して処理出来るだろうか 何うしたら ADO.NET で分散トランザクションを処理出来るだろうか OleDbCommandBuilder オブジェクトリファレンス 此の項では ADO.NET の CommandBuilder オブジェクトを取り上げたので 此処に OleDbCommandBuilder オブジェクトのプロパティとメソッドを纏めて置く CommandBuilder オブジェクトに付いては 先ず押さえて置く可き点が有る 詰まり OleDbCommandBuilder オブジェクトと SqlCommandBuilder オブジェクトは 同じ基本クラスから派生した物ではないと謂う点で有る 現に 最初のリリースの ADO.NET には CommandBuilder と謂う基本クラスが抑も存在しない バックエンドデータベースから必要なメタデータを取り込み 其のデータを更新ロジックに変換するコードを作成する事は 其れ程簡単では無い 其れが簡単で有れば 最初から CommandBuilder オブジェクト等は必要なかった筈で有る.NET データプロバイダの CommandBuilder クラスを作成するコードは決して単純では無いし 実行時に CommandBuilder オブジェクトを使えばパフォーマンスが落ちるのは解り切った事なので 幾つかのサードパーティ製の.NET データプロバイダに CommandBuilder クラスが含まれて居ないのも別段驚く可き事では無い -44-
45 OleDbCommandBuilder オブジェクトのプロパティ 表 10.1 は OleDbCommandBuilder オブジェクトのプロパティを纏めた物で有る 表 10.1 OleDbCommandBuilder オブジェクトのプロパティプロパティデータ型説明 DataAdapter DataAdapter CommandBuilder が更新ロジックを生成する対象と成る DataAdapter を返す QuotePrefix String CommandBuilder が列名とテーブル名を区切る為に使用するプレフィックス QuoteSuffix String CommandBuilder が列名とテーブル名を区切る為に使用するサフィックス DataAdapter CommandBuilder オブジェクトの DataAdapter プロパティでは CommandBuilder オブジェクトに関連付けられて居る DataAdapter を調査 / 変更出来る 此のプロパティは CommandBuilder オブジェクトのコンストラクタでも設定出来る QuotePrefix QuoteSuffix CommandBuilder オブジェクトの QuotePrefix プロパティと QuoteSuffix プロパティの値は CommandBuilder オブジェクトがクエリの中でテーブル名と列名を区切る為に使用する文字列で有る 孰れの場合も 既定値は空文字列で有る OleDbCommonBuilder オブジェクトのメソッド 表 10.2 は OleDbCommandBuilder オブジェクトのメソッドを纏めた物で有る 表 10.2 OleDbCommandBuilder オブジェクトのメソッドメソッド説明 DeriveParameters GetDeleteCommand GetInsertCommand GetUpdateCommand RefreshSchema DeriveParameters ストアドプロシージャを呼び出す Command のパラメータ情報を取り込む DataAdapter オブジェクトの DeleteCommand のロジックを含んだ Command を返す DataAdapter オブジェクトの InsertCommand のロジックを含んだ Command を返す DataAdapter オブジェクトの UpdateCommand のロジックを含んだ Command を返す CommandBuilder に対し 其の更新ロジックを生成し直す必要が有る事を通知する CommandBuilder オブジェクトの機能は DataAdapter オブジェクトの更新ロジックを生成する丈では無い CommandBuilder に依って ストアドプロシージャの為のパラメータ情報を取り込む事も出来る 次に示すのは CommandBuilder オブジェクトの DeriveParameters メソッドを使って ストアドプロシージャ呼出の為のパラメータ情報を取り込み 其のデータを書き出すコードで有る Visual Basic Dim strconn As String strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" & _ "Initial Catalog=Northwind;Trusted_Connection=Yes;" Dim cn As New OleDbConnection(strConn) Dim cmd As New OleDbCommand("CustOrdersOrders", cn) cmd.commandtype = CommandType.StoredProcedure Dim cb As New OleDbCommandBuilder( ) cn.open( ) cb.deriveparameters(cmd) -45-
46 cn.close( ) Dim param As OleDbParameter For Each param In cmd.parameters Console.WriteLine(param.ParameterName) Console.WriteLine(vbTab & param.direction.tostring) Console.WriteLine(vbTab & param.oledbtype.tostring) Console.WriteLine( ) Next param C# string strconn; strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" + "Initial Catalog=Northwind;Trusted_Connection=Yes;"; OleDbConnection cn = new OleDbConnection(strConn); OleDbCommand cmd = new OleDbCommand("CustOrdersOrders", cn); cmd.commandtype = CommandType.StoredProcedure; OleDbCommandBuilder cb = new OleDbCommandBuilder( ); cn.open( ); cb.deriveparameters(cmd); cn.close( ); foreach (OleDbParameter param in cmd.parameters) Console.WriteLine(param.ParameterName); Console.WriteLine(" t" + param.direction.tostring( )); Console.WriteLine(" t" + param.oledbtype.tostring( )); Console.WriteLine( ); ストアドプロシージャを呼び出すコマンドのパラメータコレクションを作成する時に Size Precision Scale の各プロパティに設定する値が解らなければ デザイン時に一度此の様なコードを使用すると便利で有る DeriveParameters メソッドを使用する為には 指定した Command オブジェクトの Connection が開いた状態で使用可能に成って居る必要が有る GetDeleteCommand GetInsertCommand GetUpdateCommand CommandBuilder オブジェクトの GetUpdateCommand GetInsertCommand GetDeleteCommand の各メソッドを使えば CommandBuilder が生成したロジックを確認出来る 此の 3 つのメソッドも デザイン時に役立つ 小さなサンプルアプリケーションのコードの中で CommandBuilder を作成してから其等のメソッドを使えば CommandBuilder から生成された CommandText とパラメータ情報を表示出来る 其のクエリとパラメータ情報を使った更新ロジックは 実際のコードの中で色々と活用出来る RefreshSchema アプリケーションの中で DataAdapter オブジェクトのクエリの構造を変更する場合は CommandBuilder オブジェクトの RefreshSchema メソッドを使用すると便利で有る DataAdapter オブジェクトの SelectCommand の CommandText プロパティに変更が有っても イベントは生成されない CommandBuilder オブジェクトは 更新ロジックを生成した時点で其の役割を終える DataAdapter オブジェクトのクエリの構造に変更を加えた結果 CommandBuilder に依って更新ロジックを生成し直す必要が有る場合は CommandBuilder オブジェクトの RefreshSchema メソッドを呼び出す -46-
47 RefreshSchema メソッドを呼び出しても CommandBuilder が其の時点で直ぐに更新ロジックを生成する訳では無い 寧ろ CommandBuilder の中に 現在のロジックが正確では無いと謂う事を示すフラグがセットされる丈で有る DataAdapter オブジェクトの Update メソッドか CommandBuilder オブジェクトの Get<Update/Insert/Delete>Command メソッドの孰れかを呼び出した時点で初めて CommandBuilder から更新ロジックが生成される事に成る FAQ( 良く尋ねられる質問 ) Q:DataAdapter には クエリ結果を DataSet に取り込む機能と DataSet に格納されて居る変更内容をデータベースに適用する機能の両方が有ると謂う事だが 其の 2 つの作業の為に同じオブジェクトを使用する必要が有るだろうか 若し其の必要が有るので有れば 多層構造のアプリケーションの中間層では クライアントからデータ抽出の呼出が有った時点から データ更新の呼出が有る迄の間 DataAdapter オブジェクトをずっと保持して置かなければ成らない事に成る 本当に然うなのだろうか A:DataSet にデータを取り込む作業とデータベースを更新する作業の両方に同じ DataAdapter を使う事は出来るが 其れは必須と謂う訳では無い 例えば 中間層のオブジェクトに 新しい DataSet を返すメソッドと DataSet 内の保留状態の変更内容をデータベースに適用するメソッドの 2 つが有る場合 夫々れのメソッドに別々の DataAdapter オブジェクトを使用しても構わない DataSet にデータを取り込む丈で有れば DataAdapter に更新ロジックは不要で有る 其の逆に DataAdapter を使ってデータベースを更新する丈で有れば DataAdapter に SelectCommand を定義する必要は無い DataAdapter に定義しなければ成らないのは 実際に実行する必要の有る Command オブジェクト丈で有る 例えば DataAdapter に依って新しい行をデータベースに送信する丈で有れば ( 詰まり 既存の行を変更したり削除したりする必要が無ければ ) InsertCommand を定義する丈で充分で有る 此の場合は SelectCommand UpdateCommand DeleteCommand の各プロパティに格納されて居る Command オブジェクトを実行しないので 最初から其等のプロパティを設定する必要は無い 但し 1 つの注意点が有る CommandBuilder オブジェクトを使って DataAdapter に更新ロジックを定義する場合は DataAdapter に SelectCommand を定義して置かないと CommandBuilder から更新ロジックを生成する事は出来ない Q: 結合クエリの結果を 1 つの DataTable に取り込み 其の DataTable 内のデータを変更した後 DataAdapter を使って其の変更内容をデータベースに適用し度いのだが 其の為のロジックは データアダプタ構成ウィザードでも CommandBuilder オブジェクトでも作成出来ない 何うしたら良いだろうか A: 先ず リレーショナルデータの処理 の 結合クエリ に付いて考えて観る必要が有る 孰れのコンポーネントでも其の種の更新ロジックを生成出来ないのは 結合クエリから返されるデータの変更操作が実際に何を意味するのかが ( 其等のコンポーネントに取って ) 明確ではない為で有る 例えば 注文の詳細データを抽出する為に此れ迄使って来たクエリを次の様に変更して 品目の製品名も取り込むとする -47-
48 SELECT D.OrderID, P.ProductName, D.ProductID, D.Quantity, D.UnitPrice FROM [Order Details] D, Products P WHERE D.OrderID = AND D.ProductID = P.ProductID ORDER BY P.ProductID 此のクエリの結果を DataTable に取り込んでから 1 つの行を変更した場合 データベースの内容は何う変更する事に成るのだろうか 此のクエリを記述した開発者に取って 最終的な意図は明確で有る 詰まり データベースの Order Details テーブル内の対応する行を変更すると謂う事で有る (Products テーブルの ProductName 列は 変更には影響しない参考情報に過ぎない ) 併し データアダプタ構成ウィザードや CommandBuilder オブジェクトは 其の意図を掴めない ADO カーソルエンジンは 結合クエリの場合にも自動的に更新ロジックを生成するが 其のロジックには何うしても擦れが出て来る 例えば 先程のクエリを使って ADO の Recordset を生成し Order Details テーブルに由来する列丈を変更すると ADO カーソルエンジンは Order Details テーブル内の対応する行丈を変更しようとするので 此処迄は問題無い 処が 品目の製品を変更しようと仕て ProductID 列 (Order Details テーブル ) と ProductName 列 (Products テーブル ) の両方に変更を加え 画面上で行データが正しく観える様にした場合 ADO カーソルエンジンは Order Details テーブルの ProductID 列と Products テーブルの ProductName 列を変更しようとする 此れは何う考えても 開発者の意図から擦れて居る 一方 ADO.NET は ADO の様な ブラックボックス テクノロジではない為 開発者が自分で更新ロジックを用意出来る 詰まり 此の場合に変更し度いのは Order Details テーブル丈なので ProductName 列の変更は無視する様な更新ロジックを自分で定義すれば良い訳で有る 其れでは 何うすれば其の様な更新ロジックを生成出来るだろうか 実際の結合クエリでは CommandBuilder もデータアダプタ構成ウィザードも余り役に立たないが 此の場合は ProductName 列を取り敢えず其の儘に仕て 孰れかのツールで更新ロジックを生成した後 ProductName 列の操作をクエリに追加し直すと謂う方法が有る ( 将に裏技と謂った感じでは有るが ) 処で 此の答えの冒頭で リレーショナルデータの処理 の事に触れたが 複数の DataTable オブジェクトと 1 つの DataRelation を使って結合クエリと同じ様な結果を抽出する事も出来る 此の方法には 更新ロジックが非常に簡単に成ると謂う大きなメリットも有る 各 DataTable のデータは データベース内の各テーブルに対応するので CommandBuilder でもデータアダプタ構成ウィザードでも 正しい更新ロジックを生成出来る Q: オプティミスティックな同時実行制御に付いての説明は有ったが ペシミスティックな同時実行制御の事は出て来なかった様で有る ADO.NET でペシミスティックな同時実行制御を設定するには 何うしたら良いだろうか A: ペシミスティックな同時実行制御とは 行を変更する前に其の行をロックする事を意味する DataSet の内容はデータベースから切断されて居る為 DataSet 内の行を変更する前にデータベース内のデータをロックする簡単な方法は無い 併し 其れと似た様な機能をトランザクションに依って実現出来る 例えば 画面に表示されるデータにユーザーが変更を加える前に データベース内のデータをロッ -48-
49 クすれば 其のユーザーの加える変更が正しくデータベースに反映される様に成る 其の為には トランザクションを開き 其のトランザクション内で次のクエリを実行して データベース内の対象行をロックし 他のユーザーからの変更を禁止する訳で有る SELECT * FROM [Order Details] HOLDLOCK WHERE OrderID = 此のクエリは SQL Server 2000 の構文で記述して有る 此の構文をサポートして居ないデータベースも有るので SQL Server 2000 以外のデータベースを使って居る場合は 夫々れのデータベースの資料で クエリに依るデータロックの方法を確認して欲しい 此の方法には 大きな欠点も有る 例えば ユーザーが或るデータベースアプリケーションの [ 変更の送信 ] ボタンをクリックするのを忘れた儘 台所に行ってドーナツを摘んでコーヒーを飲むとしたら 何う成るだろうか データベース内の対象行は ロックされた儘に成る ロックするデータが多くなれば成る程 亦 ロックの時間が長く成れば成る程 アプリケーションの処理能力は落ちて仕舞う Q:DataSet に BLOB 列が含まれて居る場合 其の DataSet を使ってデータベースを更新しても大丈夫だろうか A: 何のクエリで其れ程問題に成らないし 特にストアドプロシージャを使ってデータベースを変更する場合には 必要悪 で有るとも謂えるが BLOB 列が含まれて居る場合には 此れが非常に厄介で有る 何故だろうか 例えば 社員情報のデータベースの Employees テーブルに 社員の名前 ID 番号 役職 写真の各列が含まれて居るとする 問題は其の写真の列で有る 其の列には 大量のバイナリ情報 (JPEG ファイルの内容 ) が入って居るとする 此の様な状況で 総ての列を DataTable に取り込んでから 1 つの行の役職の列丈を変更したと仕ても DataAdapter は データベース内の対応する行を更新する為のクエリに総ての列の現在の値を書き出す事に成る 詰まり 文字列を値とする小さな列を修正した丈でも 其の社員の写真のバイナリデータの内容全体がデータベースに送信される事に成る訳で有る 亦 此の他にも データを複数のテーブルに分割すると謂う方法も有る ( 図 を参照 ) 此の図の様に 2 つの DataTable オブジェクトに分割し 其の 2 つの繋がりを示す DataRelation を用意する訳で有る 親 DataTable には Employees テーブルの主な列 (EmployeeID LastName FirstName) を組み込み 子 DataTable には BLOB の Photo( 写真データ ) 列と 親 DataTable との繋がりを確保する為の EmployeeID 列を組み込む 図 BLOB 列に基づいて DataTable を分割したようす -49-
50 此の様な構造の DataSet を使い 夫々れの DataTable に DataAdapter オブジェクトを 1 つ宛用意して置けば 役職の列丈を変更した場合に 更新クエリに写真の列も組み込まれると謂う事が無く成る 此れに依り 写真の列の内容が変更された場合に而巳 写真の列の内容がデータベースに送られる様に成る訳で有る 但し バイナリデータをファイルに保存し 其のファイルの位置情報をデータベースに格納して有る場合は 此処で取り上げた様な説明が当て嵌らないのは謂う迄も無い Q: 此の章には CommandBuilder を使って Order Details テーブルに変更内容を適用するコードが載って居た 其のコードを使って観たのだが キーワード 'Order.' 付近に正しくない構文が有る と謂うエラーに成る 何処が可笑しいのだろうか A: 質問に対して質問で答えるのは何うかと思うが 此処では 1 つ丈質問させて貰う テーブル名にスペースを入れるのは 一体何故だろうか 其れは然うでしょ テーブル名と列名にスペースを入れられなかったら 人生真っ暗だよ 等と言う開発者には未だ嘗て御目に懸かった事が無い データベースのテーブル名や列名が予約語其の物で有ったり テーブル名や列名にスペース等の特殊文字が含まれて居る場合 データベースでは 其の様なテーブル名や列名を区切り文字で囲む必要が有る 併し 本書の執筆時点で CommandBuilder オブジェクトはデータベースクエリに依って其の様な区切り文字を調べる事はしない CommandBuilder を使って其の様なテーブル名や列名を含むクエリの更新ロジックを生成する場合 CommandBuilder オブジェクトの QuotePrefix プロパティと QuoteSuffix プロパティの値を指定しないと 更新は失敗する 色々なバックエンドデータベースが対象なので 其等のプロパティの値を指定し度くない場合は 何うしたら良いだろうか OLE DB.NET データプロバイダを使って居る場合は データベースから実際の区切り文字を取得する為に OleDbConnection オブジェクトの GetOleDbSchemaTable メソッドを使用すると謂う方法が有る Microsoft OLE DB プロバイダに依って SQL Server Oracle Access と通信すると謂う環境では 次のコードが旨く動作するのを確認して居る Visual Basic Dim strconn, strsql As String strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" & _ "Initial Catalog=Northwind;Trusted_Connection=Yes;" strsql = "SELECT OrderID, ProductID, Quantity, UnitPrice " & _ "FROM [Order Details] WHERE OrderID = " & _ "ORDER BY ProductID" Dim cn As New OleDbConnection(strConn) Dim da As New OleDbDataAdapter(strSQL, cn) cn.open( ) Dim cb As New OleDbCommandBuilder(da) Dim tblschema As DataTable tblschema = cn.getoledbschematable(oledbschemaguid.dbinfoliterals, _ New Object( ) ) cn.close( ) tblschema.primarykey = New DataColumn( ) _ tblschema.columns("literalname") Dim row As DataRow row = tblschema.rows.find("quote_prefix") If Not row Is Nothing Then cb.quoteprefix = row("literalvalue") End If -50-
51 row = tblschema.rows.find("quote_suffix") If Not row Is Nothing Then cb.quotesuffix = row("literalvalue") End If C# string strconn, strsql; strconn = "Provider=SQLOLEDB;Data Source=(local) NetSDK;" + "Initial Catalog=Northwind;Trusted_Connection=Yes;"; strsql = "SELECT OrderID, ProductID, Quantity, UnitPrice " + "FROM [Order Details] WHERE OrderID = " + "ORDER BY ProductID"; OleDbConnection cn = new OleDbConnection(strConn); OleDbDataAdapter da = new OleDbDataAdapter(strSQL, cn); cn.open( ); OleDbCommandBuilder cb = new OleDbCommandBuilder(da); DataTable tblschema; tblschema = cn.getoledbschematable(oledbschemaguid.dbinfoliterals, new object[] ); cn.close( ); tblschema.primarykey = new DataColumn[] tblschema.columns["literalname"]; DataRow row; row = tblschema.rows.find("quote_prefix"); if (row!= null) cb.quoteprefix = row["literalvalue"]; row = tblschema.rows.find("quote_suffix"); if (row!= null) cb.quotesuffix = row["literalvalue"]; 勿論 最初から区切り文字を必要としない様なテーブル名や列名を使って居れば 此の様な問題は発生しない Q:ADO.NET の DataAdapter を使って新しい行をデータベースに送信した時の事で有る 此のデータベースには 既定値を定義した列が有るが 新しい行の其の列には 其の既定値ではなく NULL 値が入って仕舞う ADO の場合は既定値が入って居たのだが 此れは何う謂う事だろうか A:SQL Server 等のデータベースでは データベースの列に対して既定値を定義出来る ADO.NET の DataColumn オブジェクトの DefaultValue プロパティは 其のデータベース機能に厳密に対応する訳では無い 其の為 ADO.NET では データベースの既定値が自動的に生成されない様に成って居る 亦 更に別の要素も関係して居る INSERT クエリで列を省略して居たり 列値の代わりに DEFAULT キーワードを指定した場合は 新しい行の其の列に既定値が生成されるが ADO.NET の更新ロジックでは 列が省略される事も DEFAULT キーワードが使用される事も無い ADO.NET の前身の ADO では 行単位で動的に更新が生成される データベースに更新を適用する時点で生成される INSERT ステートメントでは 変更されて居ない列は省略されて居た 其の為 ADO に依ってデータベースに新しい行を追加すると 自動的に既定値が設定されたが ADO.NET で追加する行の場合は其の様に成らない ADO.NET の場合 一番簡単な解決策は 新しい行を追加した時点で目的の列に自動的に既定値を設定するコードをアプリケーションに追加する事で有る -51-
DAOの利用
DAO VB2005 で DAO を使用して Excel のデータを取得 Visual Basic 6.0 Dim DB As DAO.Database Dim RS As DAO.Recordset Dim xlfilename As String Dim xlsheetname As String xlfilename = Form1.StatusBar1.Panels(12) & Dir(Form1.StatusBar1.Panels(12)
データアダプタ概要
データベース TableAdapter クエリを実行する方法 TableAdapter クエリは アプリケーションがデータベースに対して実行出来る SQL ステートメントやストアドプロシージャで TableAdapter で型指定されたメソッドと仕て公開される TableAdapter クエリは 所有るオブジェクトのメソッドと同様に 関連付けられたメソッドを呼び出す事に依り実行出来る TableAdapter
データベースプログラミング
データセットとデータテーブル データセット (DataSet) とは何かに付いて 簡単に説明する データセットとは 典型的な.NET の Windows データベースアプリケーションでは データセットを通じてアプリケーションからデータベースにアクセスする データセットとは 簡単に謂うと データベースから取得したレコードをメモリ上に持つ為の入れ物で有る 例えば データベースからレコードを取得し 其れをグリッドコントロールに表示する様な
VB.NET解説
Visual Basic.NET データベース編 目次 ADO.NET の概要... 2 ADO.NET と ADO... 2 ADO.NET の仕組み... 4 ADO.NET の特徴... 7 データアクセスの基本... 8 Windows アプリケーションの開発手順... 8 データ接続の作成... 9 データアダプタとコネクションの作成...10 データセットの作成...14 データアクセスの利用...21
ICONファイルフォーマット
グラフィックス 画像フォーマットエンコーダパラメータ 様々なフォーマットで画像を保存 Bitmap クラスを用いる事でビットマップ JPEG GIF PNG 等様々なフォーマットの画像を読み込み操作する事が出来る 更に Bitmap クラスや Graphics コンテナを用いて描画処理等を施したイメージをファイルに保存する事も出来る 此の時 読み込めるフォーマット同様に保存するフォーマットを選択する事が出来る
正規表現応用
正規表現 正規表現を使って文字列が或る形式と一致するか調べる 指定された正規表現のパターンと一致する対象が入力文字列内で見付かるか何うかを調べるには Regex クラスの IsMatch メソッドを使用する 此処では IsMatch メソッドを使った例を幾つか紹介する 猶 正規表現のパターンと一致する個所を探し 見付かれば 其の箇所を抽出する方法は 正規表現を使って文字列を検索し 抽出する で紹介して居る
Userコントロール
User コントロール 初めてのユーザーコントロールの作成 作成したクラスは他のプログラムで再利用出来る為 同じコードを何度も繰り返し作成する必要が無い コントロールも 複数のプロジェクトで再利用出来るクラスで有る 同じユーザーインターフェイスを何度も繰り返してデザインすると謂う経験は 恐らく誰でも有る 例えば 姓と名を入力する為の TextBox コントロールを追加した後で 両方を組み合わせてフルネームを作成するコードを追加する等の作業で有る
mySQLの利用
MySQL の利用 インストール インストール時に特に注意点は無い 本稿記述時のバージョンは 6.5.4 で有る (2017 年 11 月現在では 6.10.4 で https://dev.mysql.com/downloads/connector/net/6.10.html よりダウンロード出来る ) 参照設定 インストールが終了すれば Visual Studio で参照の設定を行う 参照の設定画面で
VB.NET解説
Visual Basic.NET 印刷編 目次 印刷の概要... 2 印刷の流れ... 2 標準の Windows フォーム印刷ジョブの作成... 3 実行時に於ける Windows フォーム印刷オプションの変更... 3 Windows フォームに於ける接続されたプリンタの選択... 4 Windows フォームでのグラフィックスの印刷... 5 Windows フォームでのテキストの印刷...
TableAdapterクラス
テーブルアダプタ TableAdapter の概要 TableAdapter を使用すると アプリケーションとデータベース間で通信する事が出来る様に成る 具体的には TableAdapter は データベースに接続し クエリやストアドプロシージャを実行し 返されたデータが格納された新しいデータテーブルを返すか 返されたデータを既存の DataTable に格納する TableAdapter は 更新されたデータをアプリケーションからデータベースに送り返す場合にも使用する
ADO.NETのアーキテクチャ
データベース ADO.NET のアーキテクチャ 従来のデータ処理は 主に接続をベースとした 2 層モデルに基づいて居た 最近のデータ処理では 多階層アーキテクチャが多用される様に成った為 プログラマは 非接続型アプローチへと切り替えて アプリケーションに より優れたスケーラビリティを提供して居る ADO.NET のコンポーネント ADO.NET には データへのアクセスとデータの操作に使用出来るコンポーネントが
Visual Basic 資料 電脳梁山泊烏賊塾 コレクション初期化子 コレクション初期化子 初めに.NET 版の Visual Basic では 其れ迄の Visual Basic 6.0 とは異なり 下記の例の様に変数宣言の構文に 初期値を代入する式が書ける様に成った 其の際 1 の様に単一の値
コレクション初期化子 コレクション初期化子 初めに.NET 版の Visual Basic では 其れ迄の Visual Basic 6.0 とは異なり 下記の例の様に変数宣言の構文に 初期値を代入する式が書ける様に成った 其の際 1 の様に単一の値 ( 此処では 10) を代入する丈でなく 2 の配列変数の宣言の様に ブレース { } の中にカンマ区切りで初期値のリストを記述し 配列の各要素に初期値を代入出来る様に成った
データアダプタ概要
データベース データアダプタ データアダプタの概要 データアダプタは ADO.NET マネージプロバイダの重要な部分で有り データソースとデータセットの間の通信に使用されるオブジェクトのセットで有る マネージプロバイダには アダプタの他に接続オブジェクト データリーダーオブジェクト 及び コマンドオブジェクトが含まれる アダプタは データソースとデータセットの間でデータを交換する為に使用される 多くのアプリケーションでは
グラフィックス
グラフィックス PictureBox の Image プロパティに関する良く有る勘違い PictureBox に画像を表示する方法と仕て PictureBox の Image プロパティを使う方法と Graphics の DrawImage メソッドを使う方法が有るが 此の 2 つの方法を混同し 正しく理解して居ない事が多い様で有る 例えば 下記に列挙する様な状況が 此れに該当する 1.PictureBox
VB実用③ アクセス操作Ⅰ
VB でアクセス操作 Ⅰ VB 2005 3 プログラムの概要 此処では コントロールを使用しないで コードに依るデータベースへの接続とデータの取得の方法を学ぶ コントロールに依るデータベース接続は 簡単にデータベースを操作する事が出来ると謂う利点が有り 小規模な運用で有れば 充分な能力を発揮する 併し 中規模 大規模に成り 複雑な操作を必要とする様に成れば 力不足に成る可能性が高く成る 然う成れば
ファイル操作-バイナリファイル
ファイル操作 バイナリ ファイルを読み書きする バイナリファイル ( 即ちテキストファイル以外のファイル ) を読み書きするには FileStream クラス (System.IO 名前空間 ) を利用する FileStream クラスはファイル用のストリームをサポートするクラスで有り Stream クラス (System.IO 名前空間 ) の派生クラスの 1 つで有る 基本的には コンストラクタで指定したファイルのストリームに対して
ファイル操作
ファイル操作 TextFieldParser オブジェクト ストリームの読込と書込 Microsoft.VisualBasic.FileIO 名前空間の TextFieldParser オブジェクトは 構造化テキストファイルの解析に使用するメソッドとプロパティを備えたオブジェクトで有る テキストファイルを TextFieldParser で解析するのは テキストファイルを反復処理するのと同じで有り
プロセス間通信
プロセス間通信 プロセス間通信 (SendMessage) プロセス間通信とは 同一コンピューター上で起動して居るアプリケーション間でデータを受け渡し度い事は時々有る Framework には リモート処理 と謂う方法でデータの受け渡しを行なう方法が有る 此処では 此の方法では無く 従来の方法の API を使用したプロセス間通信を紹介する 此の方法は 送信側は API の SendMessage で送り
ハッシュテーブル
ハッシュテーブル ハッシュテーブル ( 連想配列 ) を使う ハッシュテーブルとは キー (key) と値 (value) のペアを保持して居るコレクションで有る 通常の配列がインデックス番号に依り各値 ( 各要素 ) にアクセス出来るのに比べて ハッシュテーブルでは インデックス番号の代わりにキーを用いて 其の各値にアクセスする事が出来る キーと 其のキーから連想される ( 対応付けられて居る )
VB実用Ⅲ⑩ フリーデータベースⅡ
MySQL の利用 MySQL の ODBC(MyODBC) テキストでは MySQL Connector/ODBC(mysql-connector-odbc-3.51.14-win32.msi) をインストールした場合に付いて解説して居るが 此処では MyODBC(MyODBC-3.51.10-x86-win-32bit.msi) をインストールし myodbc-3.51.06-conv_ujis.zip
ListViewコントロール
ListView コントロール ListView コントロールへ項目を追加 本稿では.NET Framework の標準コントロールで有る ListView コントロール (System.Windows.Forms 名前空間 ) を活用する為に ListView コントロールにデータを追加する方法を紹介する ListView コントロールは データ項目をアイコン表示や詳細表示等に依り一覧表示する為の物で
データベースⅠ
データベース操作プログラム Ⅰ VB 2005 8 プログラムの概要 データベースプログラムを手軽に作成する方法としては Access のフォーム機能を用いる事が 先ず考えられます ウィザードも充実しており 操作を覚えれば簡単に作成する事が出来ます 其れでは VB でデータベースプログラムを作るメリットは何んでしょうか 1 つは Access のフォーム機能に比べ コントロールが充実している事 より自由なデザインが可能です
NotifyIconコントロール
NotifyIcon コントロール システムトレイ ( タスクトレイ ) にアイコンを表示する.NET Framework 2.0 以降の場合は 後述の 2 を観て欲しい Outlook や MSN Messenger 等の様に Windows アプリケーションではシステムトレイ ( タスクトレイ ステータス領域等とも呼ばれる ) にアイコンを表示して アプリケーションの状態を示したり アプリケーションのフォームを表示したりする為のショートカットとして利用する事が出来る.NET
VB実用⑤ アクセス操作Ⅲ
VB でアクセス操作 Ⅲ VB 2005 5 プログラムの概要 前回に引き続き 此処では コードに依るデータベースの操作方法を学ぶ コネクションオブジェクトを用いてデータベースと接続し 表形式でデータを表示するデータグリッドビューコントロールにレコードデータを自動的に表示する手法を学ぶ 既に学んだ様にコントロールを連結する事に依り コードレスでデータベース操作が可能で有るが 此処では 着目して居る行
64bit環境で32bitコンポーネントの利用
64bit 環境 64bit 環境で 32bit コンポーネントの利用 Windows 7 や 8 の出現で 愈々 64bit 環境も普及し始めて来た 64bit 環境に於いては 64bit 用に最適化された 64bit コンポーネントを使用するのが本筋で有ろうが 64bit 環境に於いても 32bit コンポーネントを使用し度い場合が有る 過去の遺物 ( レガシ ) と仕て切り捨てるのではなく 良い物は良い物と仕て有効利用すると謂う観点から
XMLプログラミング(ADO編)
XML プログラミング ADO 2.5 と XML ADO 2.5 と XML ADO 2.5 を利用すれば 粗所有るデータソースからのデータの読み取りや ADO レコードセット ( メモリ上にデータを持つ仮想テーブルの様な物 ) へのデータの配置 XML へのデータ変換が可能に成る XML に変換したデータをデータストリームに配置すれば ファイルや ADO ASP Response オブジェクト
VB実用① データベースⅠ
VB でデータベース Ⅰ VB 2005 1 プログラムの概要 データベースプログラムを手軽に作成する方法と仕ては Access のフォーム機能を用いる事が 先ず考えられる ウィザードも充実して居り 操作を覚えれば簡単に作成する事が出来る 其れでは VB でデータベースプログラムを作るメリットは何かと謂えば先ず Access のフォーム機能に比べ コントロールが充実して居り より自由なデザインが可能と謂う事と
構造体
構造体 Byte 配列 構造体とコピーする方法 構造体とバイト配列の変換を行うには System.Runtime.InteropServices 名前空間をインポートして置くと便利で有る Imports System.Runtime.InteropServices using System.Runtime.InteropServices; 下記の 3 種類の構造体にバイト配列の値を格納した場合に付いて検証する
MVP for VB が語る C# 入門
MVP for VB が語る C# 入門 2008.08.09 初音玲 自己紹介 Z80 アセンブラ 6809 アセンブラ F-BASIC N88-BASIC FORTRAN 77 COBOL LISP Turbo Pascal Prolog KABA C 言語 M シリーズ アセンブラ PL/I VB3.0~ PL/SQL T-SQL VB2005/2008 index Microsoft Visual
ADOとADO.NET
ADO と ADO.NET Visual Basic.NET では 従来の ADO を従来の方法で使用する事も出来るが 通常 データベースにアクセスする為に ADO.NET を使用する 此処では Windows 上で動作するアプリケーションの開発に必要な知識を中心に ADO と ADO.NET の差異を説明する Visual Basic 6.0 では データベースにアクセスする為に ADO(ActiveX
ファイル監視
ファイル操作 ファイルやディレクトリの監視 FileSystemWatcher クラス.NET Framework のクラスライブラリには ファイルやディレクトリの作成 変更 削除を監視する為の FileSystemWatcher クラスが System.IO 名前空間に用意されて居る ( 但し Windows 98/Me では利用出来ない ) 此れを利用すると 特定のディレクトリにファイルが作成された
いまさら聞けないVB2008 ADO.NET超入門
自己紹介 Z80 アセンブラ 6809 アセンブラ F-BASIC N88-BASIC FORTRAN 77 COBOL LISP Turbo Pascal Prolog KABA C 言語 M シリーズ アセンブラ PL/I VB3.0~ PL/SQL T-SQL VB2005/2008 index 接続 データ取得 データ更新 権限 ADO.NET の基本的な構造.NET データプロバイダ Parameter
目次 更新履歴... 1 はじめに... 3 レコードセット?... 3 準備... 5 SQL でデータを取得する... 6 データのループ処理... 7 列の値を取得する... 7 対象行を変更する (MoveFirst, MoveNext, MovePrevious, MoveLast)...
レコードセット (ADODB.Recordset) の使い方 作成日 : 2018/02/05 作成者 : 西村 更新履歴 更新日 更新概要 作業者 2018/02/05 新規作成 西村 1 目次 更新履歴... 1 はじめに... 3 レコードセット?... 3 準備... 5 SQL でデータを取得する... 6 データのループ処理... 7 列の値を取得する... 7 対象行を変更する (MoveFirst,
ルーレットプログラム
ルーレットプログラム VB 2005 4 プログラムの概要 カジノの代表的なゲーム ルーレット を作成する 先ず GO! ボタンをクリックすると ルーレット盤上をボールが回転し 一定時間経過すると ボールが止まり 出目を表示するプログラムを作成する 出目を 1~16 大小 偶数奇数の内から予想して 予め設定した持ち点の範囲内で賭け点を決め 賭け点と出目に依り 1 点賭けの場合は 16 倍 其他は 2
プレポスト【問題】
コース名 : 基礎から学ぶ!Excel VBA による業務の自動化 受講日 氏名 1 Excel VBA を使用するメリットとして誤っているものを 1 つ選びなさい 1. 手作業では手間のかかる作業も プログラムに記述した処理は一括して実行されるため 何段階ものメニュー操作を行う必要がなくなる 2. プログラムに書いた処理は記述どおりに実行されるため だれがいつ何回行っても確実な処理がなされ 誤動作を防ぐことができる
グラフィックス 目次
WPF チュートリアル WPF デザイナーに依るサイズ変更可能なアプリケーションの作成 Grid コンテナーコントロールと共に GridSplitter コントロールを使用する事に依り 実行時にユーザーに依ってサイズを変更出来るウィンドウレイアウトを作成出来る 例えば 領域に分割されて居る UI を持つアプリケーションで ユーザーが分割線をドラッグする事に依り より多くの内容を見る必要が有る領域を大きくする事が出来る
PYTHON 資料 電脳梁山泊烏賊塾 PYTHON 入門 関数とメソッド 関数とメソッド Python には関数 (function) とメソッド (method) が有る モジュール内に def で定義されて居る物が関数 クラス内に def で定義されて居る物がメソッドに成る ( 正確にはクラスが
PYTHON 入門 関数とメソッド 関数とメソッド Python には関数 (function) とメソッド (method) が有る モジュール内に def で定義されて居る物が関数 クラス内に def で定義されて居る物がメソッドに成る ( 正確にはクラスがインスタンス化されてからメソッドに成る ) # 関数 def test_func(): print('call test_func') #
VB実用④ アクセス操作Ⅱ
VB でアクセス操作 Ⅱ VB 2005 4 プログラムの概要 前回に引き続き 此処では コードに依るデータベースの操作方法を学ぶ コネクションオブジェクトを用いてデータベースと接続し 表形式でデータを表示するデータグリッドビューコントロールにレコードデータを自動的に表示する手法を学ぶ 既に学んだ様にコントロールを連結する事に依り コードレスでデータベース操作が可能で有るが 此処では 着目して居る行
VB実用⑦ エクセル操作Ⅰ
VB でエクセル操作 Ⅰ VB 2005 7 プログラムの概要 事務処理に於いて Microsoft 社のスプレッドシートソフトで有るエクセルは データベースソフトで有るアクセスと共に 業界標準 (De Facto Standard) で有ると謂う事が出来る 此処では 其のエクセルを Visual Basic から操作する方法を 重点的に学ぶ 今回は Visual Basic でエクセルを利用する基本と成るオブジェクト生成と
グラフィックス 目次
WPF チュートリアル Microsoft Expression Blend を使用してボタンを作成する 此のチュートリアルでは WPF のカスタマイズされたボタンを Microsoft Expression Blend を使用して作成する手順に付いて説明する Microsoft Expression Blend の具体的な動作は Extensible Application Markup Language(XAML)
モグラ叩きプログラム
プログラミングの有用性 VB 2005 1 醒めよ!! 遣り度い事を遣るには 先ず 遣らなければ成らない事を遣る! 現に 其の事に気付いて 日々 其の 遣らなければ成らない事 に邁進して居る人間が 居る 寧ろ 其れを愉しんでさえ居る者も居る 然して 着実に 世の中が必要とする技 術を身に付けて行って居る者が居る 併し 何が 遣らなければ成らない事 なのか解らない場合は 現実に対応出来る技術力 を持ち
Microsoft Office操作(EXCEL)
VB から Excel にデータを転送する方法 概要 此の資料では Microsoft Visual Basic アプリケーションから Microsoft Excel にデータを転送する様々な方法に付いて説明する 亦 最適なソリューションの選択に役立つ様に 夫々れの方法の長所と短所に付いても説明する 詳細 Excel ブックにデータを転送するのに最も良く使用されるのは オートメーションと呼ばれる技法で有る
内容 Visual Studio サーバーエクスプローラで学ぶ SQL とデータベース操作... 1 サーバーエクスプローラ... 4 データ接続... 4 データベース操作のサブメニューコンテキスト... 5 データベースのプロパティ... 6 SQL Server... 6 Microsoft
Visual Studio サーバーエクスプローラで学ぶ SQL とデータベース操作 Access 2007 と SQL Server Express を使用 SQL 文は SQL Server 主体で解説 Access 版ノースウィンドウデータベースを使用 DBMS プログラム サーバーエクスプローラ SQL 文 実行結果 データベース エンジン データベース SQL 文とは 1 度のコマンドで必要なデータを効率よく取得するための技術といえます
ブロック パニック
ブロックパニック VB 2005 9 プログラムの概要 壁が迫り来る不思議な空間のオリジナルゲーム ブロックパニック を作成する スタートボタンをクリックし上下左右の矢印キーで白猿を移動させる スペースキーを押すと 向いて居る方向の壁が後退する 左右の壁が合わさると ゲームは終了する 一般的に 実用プログラムに比較するとゲームプログラムは 高度なテクニックを要求される事が多い 此処では ゲームプログラムを作成する事に依り
データアダプタ詳細
データベース DataAdapter クラス DataAdapter クラスの概要 DataSet へのデータの格納やデータソースの更新に使用される一連の SQL コマンドとデータベース接続を表す DataAdapter は DataSet とデータソースとの間でデータの取得と保存を行う為の ブリッジの役割を果たす DataAdapter は 此のブリッジを提供する為に DataSet 内のデータをデータソース内のデータと一致する様に変更する
Oracle Lite Tutorial
GrapeCity -.NET with GrapeCity - FlexGrid Creation Date: Nov. 30, 2005 Last Update: Nov. 30, 2005 Version: 1.0 Document Control Internal Use Only Author Hiroshi Ota Change Logs Date Author Version Change
VB 資料 電脳梁山泊烏賊塾 音声認識 System.Speech の利用 System.Speech に依るディクテーション ( 音声を文字列化 ).NetFramework3.0 以上 (Visual Studio 2010 以降 ) では 標準で System.Speech が用意されて居るの
音声認識 System.Speech の利用 System.Speech に依るディクテーション ( 音声を文字列化 ).NetFramework3.0 以上 (Visual Studio 2010 以降 ) では 標準で System.Speech が用意されて居るので 此れを利用して音声認識を行うサンプルを紹介する 下記の様な Windows フォームアプリケーションを作成する エディタを起動すると
Microsoft認定資格問題集(70-483_demo)
Microsoft 認定資格問題集 受験番号 : 70-483 受験名 : C# でのプログラミング言語 : 日本語問題数 :179 問 テストバリュー (TESTVALUE) はこの日本語版問題集の著作権を所有します 問題集の他人への展開 譲渡 転売 複製 転載等の無断行為は法律上で禁止されています 違反が発覚した場合 法的措置を取らせて頂きますので 予めご了承ください 問題 1 Orderオブジェクトのコレクションがあります
正規表現概要
正規表現 正規表現の概要 此処では.NET Framework の正規表現に付いて 主に正規表現パターンに付いて 基本を極く簡単に説明する 猶.NET Framework の正規表現は Perl5 の正規表現に対応する様にデザインされて居るので Perl の正規表現を理解して居れば問題は無い ( 勿論 理解して居なくても問題は無い ) 正規表現で出来る事 正規表現は 文字列を検索する為に使用する 正規表現を使うと
Microsoft Office操作
VB から ADO を利用して EXCEL の操作 概要 此の資料では Excel ワークシートをデータソースとして ActiveX オブジェクト (ADO) を使用する方法を説明して居る 亦 Excel 固有の構文上の問題や制限事項に付いても解説する 此の資料では OLAP やピボットテーブルテクノロジ 其の他の Excel データの特殊な使用法に付いては扱わない Excel ワークシートの行 及び
( 目次 ) 1. はじめに 開発環境の準備 仮想ディレクトリーの作成 ASP.NET のWeb アプリケーション開発環境準備 データベースの作成 データベースの追加 テーブルの作成
KDDI ホスティングサービス (G120, G200) ブック ASP.NET 利用ガイド ( ご参考資料 ) rev.1.0 KDDI 株式会社 1 ( 目次 ) 1. はじめに... 3 2. 開発環境の準備... 3 2.1 仮想ディレクトリーの作成... 3 2.2 ASP.NET のWeb アプリケーション開発環境準備... 7 3. データベースの作成...10 3.1 データベースの追加...10
印刷
CrystalReport 印刷 概要 レポート作成ツールの利点 データの取得は難しく無い 例えば コードを記述してレコードセットをループし 目的の Windows アプリケーションや Web のアプリケーションに出力出来る.NET Framework 環境では 更に其のデータを Repeater DataList 又は DataGrid 等のコントロールにバインドする事も可能で有る 併し 此のデータを一定のレポート形式で表示するには
VB実用Ⅲ⑩ フリーデータベースⅡ
フリーデータベース Ⅱ VB オートメーション 10 プログラムの概要 事務処理に於いて Microsoft Office は 業界標準 (De Facto Standard) で有ると謂う事が出来る 此の Office には データベースソフトと仕て Access が用意されて居り 現在では データベースの代名詞とも謂う可き存在で有る 古くは MS-DOS 時代に 桐 と謂うデータベースが存在したが
目次 はじめに... 3 システムの必要条件... 3 サンプルアプリケーションの作成... 3 手順 手順 手順 手順 手順 手順 終わりに... 23
SharpShooter Reports.Silverlight 基本的な使い方 Last modified on: August 16, 2012 本ドキュメント内のスクリーンショットは英語表記ですが SharpShooter Reports JP( 日本語版 ) では日本語で表示されます 目次 はじめに... 3 システムの必要条件... 3 サンプルアプリケーションの作成... 3 手順 1...
Oracle Lite Tutorial
GrapeCity -.NET with GrapeCity - InputMan Creation Date: Nov. 30, 2005 Last Update: Nov. 30, 2005 Version: 1.0 GrapeCity Microsoft Visual Studio.NET VB.NET Oracle Tips InputMan InputMan Oracle.NET Oracle
データベースプログラミング
データベース Visual Studio 2010 に依る Windows データベースプログラミング 第 1 回 DB アプリのデータソースと成るデータベースの作成 1.SQL Server の DB ファイルをプロジェクトに追加 2. データソース構成ウィザードに依る DB ファイルの追加 3. テーブルの作成とレコードの追加 第 2 回データセットとデータテーブル 1. データセットとは? 2.
データベースアクセス
データベースアクセスコンポーネント 1. 概要 データベースアクセスコンポーネントとは SQL データベースにアクセスして SQL 文を実行することによりデータベース検索を行う機能を提供するコンポーネントです また データベースアクセスコンポーネントでは データベースの構成情報 接続情報 エラー情報等を取得することも可能です データベースアクセスコンポーネントは アプリケーションビルダーのメニューから以下のように選びます
Dolteng Scaffoldに対する機能追加とマスタ-ディテールScaffoldの紹介
Dolteng Scaffold に対する機能追加 とマスタ - ディテール Scaffold の紹介 せいいち (takao) 2009/03/07 目次 Dolteng Scaffold に対する機能追加 Scaffold に関して Ruby on Rails の Scaffold RoR Scaffold と Dolteng Scaffold の比較 Scaffold のデモ Scaffold
メール送信
メール Visual Basic 2005 でのメール送信 1 System.Net.Mail 名前空間の MailMessage クラスと SmtpClient クラスを使用 メール送信機能の有るアプリケーションを作成する必要が有る場合が有る 其処で 此処では メールの送信機能を持つアプリケーション ( 図 1) の作成方法に付いて紹介する 図 1 Visual Basic 6.0 では Microsoft
チャットアプリ
チャットアプリ Socket クラスに依る TCP 非同期通信の方法 複数のクライアントが同時に接続出来る TCP を利用したクライアントサーバー型チャットアプリケーションを TcpClient と TcpListener クラスを使わずに Socket クラスを使って作る方法を紹介する 初めに 此処では 複数のクライアントが同時に接続出来る TCP を利用したクライアントサーバー型チャットアプリケーション
目次 はじめに... 3 システムの必要条件... 4 ライセンス認証... 4 アクティベーション... 6 開発... 7 手順 1. アプリケーションの作成... 7 手順 2. データソースの作成と代入... 7 手順 3. テンプレートの作成 手順 4. レポートビューアの追加
SharpShooter Reports.Win 基本的な使い方 Last modified on: November 15, 2012 本ドキュメント内のスクリーンショットは英語表記ですが SharpShooter Reports JP( 日本語版 ) では日本語で表示されま す 目次 はじめに... 3 システムの必要条件... 4 ライセンス認証... 4 アクティベーション... 6 開発...
XML(DOMガイド)
XML DOM ガイド 初心者の為の XML DOM ガイド イントロダクション 本稿は XML(eXtensible Markup Language) 形式のドキュメントからデータを受け取ろうと仕て居る Microsoft Visual Basic の開発者を対象と仕て居る 此処では XML ドキュメントから情報を取得し 其のデータを Visual Basic のソリューションに統合する方法を説明する
プログラミング基礎I(再)
山元進 クラスとは クラスの宣言 オブジェクトの作成 クラスのメンバー フィールド 変数 配列 メソッド メソッドとは メソッドの引数 戻り値 変数の型を拡張したもの 例えば車のデータベース 車のメーカー 車種 登録番号などのデータ データベースの操作 ( 新規データのボタンなど ) プログラムで使う部品の仕様書 そのクラスのオブジェクトを作ると初めて部品になる 継承 などの仕組みにより カスタマイズが安全
.NETプログラマー早期育成ドリル ~VB編 付録 文法早見表~
.NET プログラマー早期育成ドリル VB 編 付録文法早見表 本資料は UUM01W:.NET プログラマー早期育成ドリル VB 編コードリーディング もしくは UUM02W:.NET プログラマー早期育成ドリル VB 編コードライティング を ご購入頂いた方にのみ提供される資料です 資料内容の転載はご遠慮下さい VB プログラミング文法早見表 < 基本文法 > 名前空間の定義 Namespace
印刷
印刷 ローカル処理モードでの SQL Server データベースと ReportViewer コントロールの使用 此のチュートリアルでは Microsoft Visual Studio 2005 Windows アプリケーションプロジェクトでレポートを作成する方法と アプリケーションのユーザーがレポートを表示出来る様に ReportViewer コントロールを Windows フォームに追加する方法に付いて説明する
マクロの実行許可設定をする方法 Excel2010 で 2010 でマクロを有効にする方法について説明します 参考 URL:
マクロの実行許可設定をする方法 Excel2010 で 2010 でマクロを有効にする方法について説明します 参考 URL: http://excel2010.kokodane.com/excel2010macro_01.htm http://span.jp/office2010_manual/excel_vba/basic/start-quit.html Excel2010 でマクロを有効にする
Microsoft Excel操作
Microsoft Excel 操作 Excel ファイルにアクセス リフレクションを利用したレイトバインディングで Excel ファイルを操作 Visual Basic なら CreatObject 関数を使用して 暗黙の遅延バインディングを利用する事に依り 簡単にに実現出来る Excel の操作も C# で実現するには 少し面倒臭い事に成る 事前バインディングでも実装する事も出来るが 事前バインディングだと
エクセル詳細 アドイン
Microsoft Excel 詳細 アドイン Excel アドインの作成 Excel アドインを作成するには ブックを作成し コード ユーザー設定ツールバー 及び メニュー項目を追加して Excel アドインファイルとして保存する 1. 新しいブックを作成してコードを追加し ユーザー設定ツールバー 又は メニューバーを作成する 2.[ ファイル ] メニューの [ プロパティ ] をクリックする
ファイル操作
データセット DataSet クラス データのメモリ内キャッシュを表す 名前空間 :System.Data アセンブリ :System.Data(system.data.dll 内 ) 解説 データソースから取得されたデータのメモリ内キャッシュで有る DataSet は ADO.NET アーキテクチャの主要コンポーネントで有る DataSet は DataRelation オブジェクトと相互に関連付ける事が出来る
C# bit 環境用 C# 2005 基礎 Ⅱ 電脳梁山泊烏賊塾 データベース操作プログラム Ⅰ C# プログラムの概要 データベースプログラムを手軽に作成する方法としては Access のフォーム機能を用いる事が 先ず考えられます ウィザードも充実しており 操作を覚
データベース操作プログラム Ⅰ C# 2005 8 プログラムの概要 データベースプログラムを手軽に作成する方法としては Access のフォーム機能を用いる事が 先ず考えられます ウィザードも充実しており 操作を覚えれば簡単に作成する事が出来ます 其れでは VB でデータベースプログラムを作るメリットは何んでしょうか 1 つは Access のフォーム機能に比べ コントロールが充実している事 より自由なデザインが可能です
Oracle Lite Tutorial
GrapeCity -.NET with GrapeCity - SPREAD Creation Date: Nov. 30, 2005 Last Update: Nov. 30, 2005 Version: 1.0 Document Control Internal Use Only Author Hiroshi Ota Change Logs Date Author Version Change
