データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server...

41
VS 2005 資料 【電脳梁山泊 烏賊塾】 -1- DataAdapter クラス DataAdapter クラスの概要 DataSet へのデータの格納やデータソースの更新に使用される一連の SQL コマンドとデータベース接 続を表す。 DataAdapter は、 DataSet とデータソースとの間でデータの取得と保存を行う為の、ブリッジの役割を 果たす。 DataAdapter は、此のブリッジを提供する為に、 DataSet 内のデータをデータソース内のデー タと一致する様に変更する Fill と、データソース内のデータを DataSet 内のデータと一致する様に変 更する Update で、割り当てを行う。 SQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや SqlConnection オブジェクトと共に SqlDataAdapter を使用する事に依り、全体的なパフォーマンスを 向上させる事が出来る。OLE DB をサポートするデータソースに接続する場合は、関連付けられた OleDbCommand オブジェクトや OleDbConnection オブジェクトと共に DataAdapter を使用する。 ODBC をサポートするデータソースに接続する場合は、関連付けられた OdbcCommand オブジェクト OdbcConnection オブジェクトと共に DataAdapter を使用する。Oracle データベースに接続する場 合は、関連付けられた OracleCommand オブジェクトや OracleConnection オブジェクトと共に DataAdapter を使用する。 DataAdapter のインスタンスを作成すると、読み書き可能プロパティが初期値に設定される。此等の初 期値の一覧に付いては、DataAdapter コンストラクタの記述を参照され度い。 コンストラクタ DataAdapter クラスの新しいインスタンスを初期化する。 オーバーロードの一覧 .NET Compact Framework に依ってサポートされて居る。 名前 説明 DataAdapter ( ) DataAdapter クラスの新しいインスタンスを初期化する。 DataAdapter (DataAdapter) 既存の同じ型のオブジェクトから、 DataAdapter クラスの新しいインスタンス を初期化する。 パブリックプロパティ 名前 説明 AcceptChangesDuringFill Fill 操作中に DataTable DataRow が追加された後で、其の行に対して AcceptChanges を呼び出すか何うかを示す値を取得・設定する。 AcceptChangesDuringUpdate Update AcceptChanges が呼び出されるか何うかを取得・設定する。 Container Component を格納して居る IContainer を取得する。

Transcript of データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server...

Page 1: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-1-

■ DataAdapter クラス ■

■ DataAdapter クラスの概要

DataSet へのデータの格納やデータソースの更新に使用される一連の SQL コマンドとデータベース接

続を表す。

DataAdapter は、DataSet とデータソースとの間でデータの取得と保存を行う為の、ブリッジの役割を

果たす。DataAdapter は、此のブリッジを提供する為に、DataSet 内のデータをデータソース内のデー

タと一致する様に変更する Fill と、データソース内のデータを DataSet 内のデータと一致する様に変

更する Update で、割り当てを行う。

SQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

SqlConnection オブジェクトと共に SqlDataAdapter を使用する事に依り、全体的なパフォーマンスを

向上させる事が出来る。OLE DB をサポートするデータソースに接続する場合は、関連付けられた

OleDbCommand オブジェクトや OleDbConnection オブジェクトと共に DataAdapter を使用する。

ODBC をサポートするデータソースに接続する場合は、関連付けられた OdbcCommand オブジェクト

や OdbcConnection オブジェクトと共に DataAdapter を使用する。Oracle データベースに接続する場

合は、関連付けられた OracleCommand オブジェクトや OracleConnection オブジェクトと共に

DataAdapter を使用する。

DataAdapter のインスタンスを作成すると、読み書き可能プロパティが初期値に設定される。此等の初

期値の一覧に付いては、DataAdapter コンストラクタの記述を参照され度い。

■ コンストラクタ

DataAdapter クラスの新しいインスタンスを初期化する。

オーバーロードの一覧

.NET Compact Framework に依ってサポートされて居る。

名前 説明

DataAdapter ( ) DataAdapter クラスの新しいインスタンスを初期化する。

DataAdapter (DataAdapter) 既存の同じ型のオブジェクトから、DataAdapter クラスの新しいインスタンス

を初期化する。

■ パブリックプロパティ

名前 説明

AcceptChangesDuringFill Fill 操作中に DataTable に DataRow が追加された後で、其の行に対して

AcceptChanges を呼び出すか何うかを示す値を取得・設定する。

AcceptChangesDuringUpdate Update で AcceptChanges が呼び出されるか何うかを取得・設定する。

Container Component を格納して居る IContainer を取得する。

デデーータタベベーースス

Page 2: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-2-

ContinueUpdateOnError 行の更新中にエラーが発生した時に、例外を生成するか何うかを指定する値を取

得・設定する。

FillLoadOption アダプタが DbDataReader から DataTable にデータを読み込む方法を決定する

LoadOption を取得・設定する。

MissingMappingAction 一致するテーブルや列が受信データに含まれて居ない場合に実行するアクション

を決定する。

MissingSchemaAction 既存の DataSet スキーマが受信データと一致しない時に実行するアクションを決

定する。

ReturnProviderSpecificTypes Fill メソッドがプロバイダ固有の値、又は、共通の CLS 準拠の値の孰れを返すか

を取得・設定する。

Site Component の ISite を取得・設定する。

TableMappings ソーステーブルと DataTable との間のマスターマップを提供するコレクションを

取得する。

■ パブリックメソッド

名前 説明

CreateObjRef リモートオブジェクトとの通信に使用するプロキシの生成に必要

な情報を総て格納して居るオブジェクトを作成する。

Dispose DataAdapter に依って使用されて居るリソースを解放する。

Equals 2 つの Object インスタンスが等しいか何うかを判断する。

Fill DataSet 名を使用して、データソース内の行と一致する様に、

DataSet 内の行を追加・更新する。亦、DataTable を作成する。

FillSchema DataTable を指定した DataSet に追加する。

GetFillParameters SQL SELECT ステートメントの実行時にユーザーが設定したパラ

メータを取得する。

GetHashCode 特定の型のハッシュ関数と仕て機能する。GetHashCode は、ハッ

シュアルゴリズムや、ハッシュテーブルの様なデータ構造での使用

に適して居る。

GetLifetimeService 対象のインスタンスの有効期間ポリシーを制御する現在の有効期

間サービスオブジェクトを取得する。

GetType 現在のインスタンスの Type を取得する。

InitializeLifetimeService 対象のインスタンスの有効期間ポリシーを制御する有効期間サー

ビスオブジェクトを取得する。

ReferenceEquals 指定した複数の Object インスタンスが同一か何うかを判断する。

ResetFillLoadOption FillLoadOption を 既 定 の 状 態 に リ セ ッ ト し 、 Fill で

AcceptChangesDuringFill を受け入れる様にする。

ShouldSerializeAcceptChangesDuringFill AcceptChangesDuringFill プロパティを永続化する必要が有るか

何うかを決定する。

ShouldSerializeFillLoadOption FillLoadOption プロパティを永続化する必要が有るか何うかを決

定する。

ToString Component の名前を格納して居る String を返す(存在する場合)。

此のメソッドはオーバーライド出来ない。

Update 指定された DataSet 内の "Table" と謂う名前の DataTable に含ま

れる挿入行、更新行、又は、削除行に対して、INSERT、UPDATE、

又は、DELETE ステートメントを個別に呼び出す。

Page 3: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-3-

■ プロテクトメソッド

名前 説明

CloneInternals 此の DataAdapter のインスタンスのコピーを作成する。

CreateTableMappings 新しい DataTableMappingCollection を作成する。

Dispose DataAdapter に依って使用されて居るリソースを解放する。

Fill DataSet 名を使用して、データソース内の行と一致する様に、DataSet 内の行を

追加・更新する。亦、DataTable を作成する。

FillSchema DataTable を指定した DataSet に追加する。

Finalize Component がガベージコレクションに依ってクリアされる前に、アンマネージリ

ソースを解放し、其の他のクリーンアップ操作を実行する。

GetService Component や其の Container で提供されるサービスを表すオブジェクトを返す。

HasTableMappings DataTableMappingCollection が作成されて居るか何うかを示す。

MemberwiseClone

OnFillError Fill 中にエラーが発生した時に呼び出される。

ShouldSerializeTableMappings 1 つ以上の DataTableMapping オブジェクトが存在し、其等を永続化する必要が

有るか何うかを確認する。

■ パブリックイベント

名前 説明

Disposed コンポーネントの Disposed イベントを待機するイベントハンドラを追加する。

FillError 格納操作中にエラーが発生した時に返される。

Page 4: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-4-

■ DataAdapter クラスの利用 ■

■ 此のセクションの内容

DataAdapter からの DataSet の読み込み(4 頁)

DataAdapter を使用して DataSet にテーブル、列、及び、行を設定する方法に付いて説明する。

DataAdapter に依るパラメータの使用(13 頁)

DataAdapter のコマンドプロパティのパラメータを使用する方法と、DataSet の列の内容をコマンド

パラメータに割り当てる方法に付いて説明する。

パラメータと戻り値の指定(19 頁)

DataAdapter 及び DataSet を使用した、入力、出力、及び、ストアドプロシージャの戻り値の使い方

に付いて説明する。

DataAdapter イベントの使用(34 頁)

DataAdapter イベントと、其等のイベントを使用する方法に付いて説明する。

DataSet への既存の制約の追加(32 頁)

既存の制約を DataSet に追加する方法に付いて説明する。

ID 値と Autonumber 値の取得(37 頁)

Microsoft SQL Server テーブルの identity 列、又は、Microsoft Access テーブルの Autonumber フィ

ールド用に生成された値を、テーブルの挿入行の列に割り当てる例を示する。

DataTable と DataColumn のマップの設定(11 頁)

DataAdapter の DataTableMappings と ColumnMappings のセットアップ方法に付いて説明する。

DataAdapter に依るデータソースの更新(23 頁)

DataSet のデータに加えた変更を解決してデータソースに戻る方法に付いて説明する。

クエリ結果のページング(9 頁)

DataAdapter に依るバッチ更新の実行(21 頁)

DataAdapter に依る UDT 列の更新(27 頁)

■ DataAdapter からの DataSet の読み込み

ADO.NET の DataSet は、データソースに依存しない一貫したリレーショナルプログラミングモデルを

提供するメモリ常駐型のデータ表現で有る。DataSet は、テーブル、制約、及び、テーブル間のリレー

ションシップを含む完全なデータのセットを表す。DataSet は、データソースとは独立して居る為、

DataSet には其のアプリケーションに固有のデータと複数のデータソースからのデータを含める事が

出来る。既存のデータソースとの対話は、DataAdapter に依って制御される。

DataAdapter の SelectCommand プロパティは、データソースからデータを取得する Command オブ

ジェクトで有る。DataAdapter の InsertCommand、UpdateCommand、DeleteCommand の各プロパ

ティは、DataSet のデータに対して行われた変更に基づいてデータソースのデータ更新を管理する

Command オブジェクトで有る。此等のプロパティに付いては、「DataAdapter に依るデータソースの

更新」で更に詳しく説明する。

Page 5: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-5-

DataAdapter の Fill メソッドは、DataAdapter の SelectCommand の結果を使用して DataSet を設定

する為に使用する。Fill は、自らの引数と仕て、設定対象で有る DataSet と、DataTable オブジェクト

(詰まり、SelectCommand が返した行を格納して居る DataTable の名前)を受け取る。

Fill メソッドは、DataReader オブジェクトを暗黙的に使用して DataSet 内でテーブルを作成する為の

列の名前と型、及び、DataSet 内のテーブルの行を設定する為のデータを返す。テーブルや列は、存在

しない場合に丈作成される。既に存在する場合は、Fill は既存の DataSet スキーマを使用する。列の型

は、「.NET Framework データプロバイダのデータ型から.NET Framework のデータ型への変換」の表

に基づき .NET Framework の型と仕て作成される。データソースに主キーが存在し、

DataAdapter.MissingSchemaActionがMissingSchemaAction.AddWithKeyに設定されて居る場合丈、

主キーが作成されるが、其れ以外の場合は主キーは作成されない。Fill はテーブルに主キーが有る事が

解ると、主キー列の値がデータソースから返された主キー列の値と一致する行に付いて、データソース

から返されたデータで DataSet 内のデータを上書きする。主キーが見付からない場合は、DataSet のテ

ーブルの末尾にデータを追加する。Fill は DataSet にデータを読み込む時に存在する総てのマッピング

を使用する(「DataTable と DataColumn のマップの設定」を参照され度い)。

※ SelectCommand が OUTER JOIN の結果を返す場合、DataAdapter は、生成される DataTable に

PrimaryKey 値を設定しない。開発者が PrimaryKey を定義して、重複行が正しく解決される様に

する必要が有る。詳細に付いては、「テーブルの主キーの定義」を参照され度い。

Microsoft SQL Server Northwind データベースへの SqlConnection を使用し DataSet 内の DataTable

に顧客リストを読み込む SqlDataAdapter のインスタンスを作成するコード例を次に示す。

SqlDataAdapter コンストラクタに渡される SQL ステートメントと SqlConnection 引数は、

SqlDataAdapter の SelectCommand プロパティを作成する為に使用される。

Visual Basic

' 変数 connection は、有効な SqlConnection オブジェクトとする。

Dim queryString As String = "SELECT CustomerID, CompanyName FROM dbo.Customers"

Dim adapter As SqlDataAdapter = New SqlDataAdapter( queryString, connection)

Dim customers As DataSet = New DataSet

adapter.Fill(customers, "Customers")

C#

// 変数 connection は、有効な SqlConnection オブジェクトとする。

string queryString = "SELECT CustomerID, CompanyName FROM dbo.Customers";

SqlDataAdapter adapter = new SqlDataAdapter(queryString, connection);

DataSet customers = new DataSet( );

adapter.Fill(customers, "Customers");

※ 此のコード例では、Connection の開始と終了を明示的に行って居ない。Fill メソッドは、接続が未

だ開いて居ない事を認識すると DataAdapter が使用して居る Connection を暗黙的に開く。Fill が

接続を開いた場合は、Fill の終了時に Fill が接続を終了する。此れに依り、Fill や Update 等の単一

の操作を扱う場合にコードを簡略化出来る。此れに対し、開いて居る接続を必要とする複数の操作

を実行する場合は、Connection の Open メソッドを明示的に呼び出し、データソースに対する操作

の実行後にConnectionのCloseメソッドを呼び出す事でアプリケーションのパフォーマンスを改善

出来る。リソースを解放して他のクライアントアプリケーションが使用出来る様にする為に、デー

タソースへの接続を開いた儘にする時間は最小限にする事を推奨する。

Page 6: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-6-

複数結果セット

DataAdapter は複数の結果セットを検出すると、DataSet に複数のテーブルを作成する。此等のテーブ

ルには、Table0 の様に、"Table" で始まるインクリメンタル既定名 TableN が割り当てられる。テーブ

ル名を引数と仕て Fill メソッドに渡すと、TableName0 を表す "TableName" で始まるインクリメンタ

ル既定名 TableNameN が割り当てられる。

複数の DataAdapter からの DataSet の読み込み

1 つの DataSet で、任意の数の DataAdapter オブジェクトを使用出来る。夫々れの DataAdapter で 1

つ以上の DataTable オブジェクトにデータを格納し、関連するデータソースに更新を反映させる事が出

来る。DataSet に対して、DataRelation オブジェクトや Constraint オブジェクトを部分的に追加出来

る為、開発者は複数の異なるデータソースから取得したデータを関連付ける事が出来る。例えば、

Microsoft SQL Server データベース、OLE DB を通じて公開される IBM DB2 データベース、及び、

XML をストリーム転送するデータソースからのデータを DataSet に含める事が出来る。1 つ以上の

DataAdapter オブジェクトを使用して、各データソースとの通信を行う事が出来る。

次のコード例では、Microsoft SQL Server 2000 の Northwind データベースからの顧客リストと

Microsoft Access 2000 に格納されて居る Northwind データベースからの注文リストを取得する。デー

タが格納されて居るテーブルが DataRelation に関連付けられ、顧客の一覧が各顧客の注文と共に表示

される。DataRelation オブジェクトの詳細に付いては、「テーブル間のリレーションシップの追加」、

及び、「テーブル間のリレーションシップの移動」を参照され度い。

Visual Basic

' 変数 customerConnection は、有効な SqlConnection オブジェクトとする。

' 変数 orderConnection は、有効な OleDbConnection オブジェクトとする。

Dim custAdapter As SqlDataAdapter = New SqlDataAdapter( _

"SELECT * FROM dbo.Customers", customerConnection)

Dim ordAdapter As OleDbDataAdapter = New OleDbDataAdapter( _

"SELECT * FROM Orders", orderConnection)

Dim customerOrders As DataSet = New DataSet( )

custAdapter.Fill(customerOrders, "Customers")

ordAdapter.Fill(customerOrders, "Orders")

Dim relation As DataRelation = _

customerOrders.Relations.Add("CustOrders", _

customerOrders.Tables("Customers").Columns("CustomerID"), _

customerOrders.Tables("Orders").Columns("CustomerID"))

Dim pRow, cRow As DataRow

For Each pRow In customerOrders.Tables("Customers").Rows

Console.WriteLine(pRow("CustomerID").ToString( ))

For Each cRow In pRow.GetChildRows(relation)

Console.WriteLine(vbTab & cRow("OrderID").ToString( ))

Next

Next

Page 7: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-7-

C#

// 変数 customerConnection は、有効な SqlConnection オブジェクトとする。

// 変数 orderConnection は、有効な OleDbConnection オブジェクトとする。

// Assumes that customerConnection is a valid SqlConnection object.

// Assumes that orderConnection is a valid OleDbConnection object.

SqlDataAdapter custAdapter = new SqlDataAdapter(

"SELECT * FROM dbo.Customers", customerConnection);

OleDbDataAdapter ordAdapter = new OleDbDataAdapter(

"SELECT * FROM Orders", orderConnection);

DataSet customerOrders = new DataSet( );

custAdapter.Fill(customerOrders, "Customers");

ordAdapter.Fill(customerOrders, "Orders");

DataRelation relation = customerOrders.Relations.Add("CustOrders",

customerOrders.Tables["Customers"].Columns["CustomerID"],

customerOrders.Tables["Orders"].Columns["CustomerID"]);

foreach (DataRow pRow in customerOrders.Tables["Customers"].Rows)

{

Console.WriteLine(pRow["CustomerID"]);

foreach (DataRow cRow in pRow.GetChildRows(relation))

Console.WriteLine("¥t" + cRow["OrderID"]);

}

SQL Server の 10 進数型

既定では、DataSet は、.NET Framework のデータ型を使用してデータを格納する。殆どのアプリケ

ーションで、此等のデータ型を使用してデータソース情報を簡単に表示出来る。併し、データソースの

データ型が SQL Server の 10 進数データ型や数値データ型の場合は、此の表現に依って問題が生じる

場合が有る。.NET Frameworkのdecimalデータ型の最大有効桁数は28桁で有るのに対し、SQL Server

の decimal データ型の有効桁数は 38 桁で有る。Fill が動作して居る間に、SqlDataAdapter が、SQL

Serverのdecimalフィールドの有効桁数が28文字を超えて居ると判断した場合、現在の行はDataTable

に追加されない。其の場合は FillError イベントが発生する為、開発者は有効桁数の消失が発生して居

ないか何うかを確認し、適切に対応出来る。FillError イベントの詳細に付いては、「DataAdapter イベ

ントの使用」を参照され度い。SQL Server の decimal 値を取得するには、SqlDataReader オブジェク

トを使用し、GetSqlDecimal メソッドを呼び出す事も出来る。

ADO.NET 2.0 では、DataSet の System.Data.SqlTypes に対するサポート機能が強化されて居る。詳

細に付いては、「SqlTypes と DataSet」を参照され度い。

OLE DB のチャプタ

階層構造の行セット、詰まり、チャプタ(OLE DBではDBTYPE_HCHAPTER型、ADOでは adChapter

型)を使用して DataSet の内容を格納出来る。OleDbDataAdapter が Fill が動作して居る間にチャプ

タ列を検出すると、其のチャプタ列の為の DataTable を作成し、チャプタから取得した列と行を此のテ

ーブルに格納する。チャプタ列用に作成されたテーブルには、親テーブルの名前とチャプタ列の名前の

両方を使用した "ParentTableNameChapteredColumnName" 形式の名前が割り当てられる。DataSet

にチャプタ列の名前と一致するテーブルが既に存在する場合は、現在のテーブルにチャプタデータが格

納される。既存のテーブルにチャプタ内の列と一致する列が存在しない場合は、新しい列が追加される。

Page 8: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-8-

DataSet 内のテーブルにチャプタ列のデータを格納する前に、親テーブルと子テーブルの両方に 1 つの

整数列を追加し、親列を自動インクリメントに設定し、両方のテーブルに追加された列を使用して

DataRelation を作成すると、階層構造の行セットを形成して居る親テーブルと子テーブルの間にリレ

ーションが作成される。追加されたリレーションには親テーブルの名前とチャプタ列の名前を使用した

"ParentTableNameChapterColumnName" 形式の名前が割り当てられる。

関連付けられた列は、DataSet 丈に存在する。其のデータソースからの次の Fill 操作を実行すると、変

更を既存の行にマージするのではなく、テーブルに新しい行が追加される。

DataTable を受け取る DataAdapter.Fill オーバーロードを使用した場合は、其のテーブル丈にデータ

が格納される。自動インクリメント整数列は、引き続き其のテーブルに追加されるが、子テーブルの作

成、子テーブルへのデータの格納、及び、リレーションの作成は行われない。

MSDataShapeプロバイダを使用して顧客リスト内の各顧客に対応するオーダー列を生成する例を次に

示す。チャプタ列を生成した後で、1 つの DataSet 内に其のデータを格納する。

Visual Basic

Using connection As OleDbConnection = New OleDbConnection( _

"Provider=MSDataShape;Data Provider=SQLOLEDB;" & _

"Data Source=(local);Integrated " & _

"Security=SSPI;Initial Catalog=northwind")

Dim adapter As OleDbDataAdapter = New OleDbDataAdapter( _

"SHAPE {SELECT CustomerID, CompanyName FROM Customers} " & _

"APPEND ({SELECT CustomerID, OrderID FROM Orders} AS Orders " & _

"RELATE CustomerID TO CustomerID)", connection)

Dim customers As DataSet = New DataSet( )

adapter.Fill(customers, "Customers")

End Using

C#

using (OleDbConnection connection = new OleDbConnection(

"Provider=MSDataShape;Data Provider=SQLOLEDB;" +

"Data Source=(local);Integrated Security=SSPI;Initial Catalog=northwind"))

{

OleDbDataAdapter adapter = new OleDbDataAdapter(

"SHAPE {SELECT CustomerID, CompanyName FROM Customers} " +

"APPEND ({SELECT CustomerID, OrderID FROM Orders} AS Orders " +

"RELATE CustomerID TO CustomerID)", connection);

DataSet customers = new DataSet( );

adapter.Fill(customers, "Customers");

}

Fill 操作が完了すると、DataSet に Customers と CustomersOrders の 2 つのテーブルが格納される。

CustomersOrders はチャプタ列を表す。Orders と謂う列が Customers テーブルに追加され、

CustomersOrdersと謂う列がCustomersOrdersテーブルに追加される。CustomersテーブルのOrders

列は、自動インクリメントに設定される。親テーブルで有る Customers テーブルに追加された列を使

用して、CustomersOrders と謂う DataRelation が作成される。サンプル結果の一部を次の表に示す。

Page 9: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-9-

TableName:Customers

CustomerID CompanyName Orders

ALFKI Alfreds Futterkiste 0

ANATR Ana Trujillo Emparedados y helados 1

TableName:CustomersOrders

CustomerID OrderID CustomersOrders

ALFKI 10643 0

ALFKI 10692 0

ANATR 10308 1

ANATR 10625 1

■ クエリ結果のページング

クエリ結果のページングとは、クエリ結果をデータの小さなサブセット、詰まり、ページに分けて返す

プロセスで有る。クエリ結果のページングは、結果を管理し易い小さな単位でユーザーに表示する為に

行われる一般的な処理で有る。

DataAdapter には、Fill メソッドのオーバーロードを通じて 1 ページ分のデータ丈を返す機能が用意さ

れて居る。併し、此れは大きなクエリ結果のページングには適して居ない。DataAdapter が目的の

DataTable、又は、DataSet に、要求されたレコード丈を格納する一方で、クエリ全体を返す為のリソ

ースが使用される為で有る。クエリ全体を返す必要が有るリソースを使用せずにデータソースから 1 ペ

ージ分のデータを返すには、必要な行丈返す様に限定する抽出条件をクエリに追加する。

Fill メソッドを使用して 1 ページ分のデータを返すには、データページの先頭レコードを指定する

startRecord パラメータとデータページのレコード数を指定する maxRecords パラメータを指定する。

Fill メソッドを使用してクエリ結果の最初のページ(ページサイズ:5 つのレコード)を返す方法を次

のコード例に示す。

Visual Basic

Dim currentIndex As Integer = 0

Dim pageSize As Integer = 5

Dim orderSQL As String = "SELECT * FROM dbo.Orders ORDER BY OrderID"

' 変数 connection は、有効な SqlConnection オブジェクトとする。

Dim adapter As SqlDataAdapter = New SqlDataAdapter(orderSQL, connection)

Dim dataSet As DataSet = New DataSet( )

adapter.Fill(dataSet, currentIndex, pageSize, "Orders")

C#

int currentIndex = 0;

int pageSize = 5;

string orderSQL = "SELECT * FROM Orders ORDER BY OrderID";

// 変数 connection は、有効な SqlConnection オブジェクトとする。

SqlDataAdapter adapter = new SqlDataAdapter(orderSQL, connection);

DataSet dataSet = new DataSet( );

adapter.Fill(dataSet, currentIndex, pageSize, "Orders");

Page 10: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-10-

上記の例では、DataSet に 5 つのレコード丈が格納されるが、Orders テーブル全体が返される。DataSet

に此れと同じ 5 つのレコードを格納し、5 つのレコード丈を返すには、次のコード例に示す様に SQL

ステートメントで TOP 句と WHERE 句を使用する。

Visual Basic

Dim pageSize As Integer = 5

Dim orderSQL As String = "SELECT TOP " & pageSize & _

" * FROM Orders ORDER BY OrderID"

Dim adapter As SqlDataAdapter = New SqlDataAdapter(orderSQL, connection)

Dim dataSet As DataSet = New DataSet( )

adapter.Fill(dataSet, "Orders")

C#

int pageSize = 5;

string orderSQL = "SELECT TOP " + pageSize + " * FROM Orders ORDER BY OrderID";

SqlDataAdapter adapter = new SqlDataAdapter(orderSQL, connection);

DataSet dataSet = new DataSet( );

adapter.Fill(dataSet, "Orders");

此の方法でクエリ結果をページングする時は、次のレコードページを返すコマンドに一意の ID を渡す

為に、行を順序付けする固有の識別子を保存する必要が有る。次のコード例で示す。

Visual Basic

Dim lastRecord As String = _

dataSet.Tables("Orders").Rows(pageSize - 1)("OrderID").ToString( )

C#

string lastRecord =

dataSet.Tables["Orders"].Rows[pageSize - 1]["OrderID"].ToString( );

startRecord パラメータと maxRecords パラメータを受け取る Fill メソッドのオーバーロードを使用し

て次のレコードページを返すには、現在のレコードインデックスをページサイズの分丈インクリメント

し、テーブルにレコードページを格納する。DataSet に 1 ページ分のレコード丈を追加する場合でも、

データベースサーバーはクエリ結果全体を返す事に注意され度い。次のデータページを格納する前にテ

ーブル行をクリアするコード例を次に示す。データベースサーバーとの遣り取りを減らす為に、ローカ

ルのキャッシュに、返された一定数の行を保存する事も出来る。

Visual Basic

currentIndex = currentIndex + pageSize

dataSet.Tables("Orders").Rows.Clear( )

adapter.Fill(dataSet, currentIndex, pageSize, "Orders")

C#

currentIndex += pageSize;

dataSet.Tables["Orders"].Rows.Clear( );

adapter.Fill(dataSet, currentIndex, pageSize, "Orders");

Page 11: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-11-

データベースサーバーに依ってクエリ全体を返さずに次のレコードページを返すには、SELECT ステー

トメントに限定的な抽出条件を指定する。上の例では最後に返されたレコードが保存されるが、次のコ

ード例に示す様に其のレコードを WHERE 句で使用するとクエリの開始点を指定出来る。

Visual Basic

orderSQL = "SELECT TOP " & pageSize & _

" * FROM Orders WHERE OrderID > " & lastRecord & " ORDER BY OrderID"

adapter.SelectCommand.CommandText = orderSQL

dataSet.Tables("Orders").Rows.Clear( )

adapter.Fill(dataSet, "Orders")

C#

orderSQL = "SELECT TOP " + pageSize +

" * FROM Orders WHERE OrderID > " + lastRecord + " ORDER BY OrderID";

adapter.SelectCommand.CommandText = orderSQL;

dataSet.Tables["Orders"].Rows.Clear( );

adapter.Fill(dataSet, "Orders");

■ DataTable と DataColumn のマップの設定

DataAdapter は、TableMappings プロパティに 0 個以上の DataTableMapping オブジェクトのコレク

ションを持って居る。DataTableMapping は、データソースに対するクエリで返されたデータと

DataTable の間のマスタのマップを提供する。DataTableMapping 名は、DataAdapter の Fill メソッ

ドに DataTable 名の代わりと仕て渡す事が出来る。Authors テーブルに対して AuthorsMapping と謂

う名前の DataTableMapping を作成する例を次に示す。

Visual Basic

workAdapter.TableMappings.Add("AuthorsMapping", "Authors")

C#

workAdapter.TableMappings.Add("AuthorsMapping", "Authors");

DataTableMapping を使用すると、DataTable 内でデータベースの列名とは異なる列名を使用出来る。

DataAdapter は、テーブルの更新時に此のマップを使用して列を一致させる。

DataAdapter の Fill メソッドや Update メソッドを呼び出す時に TableName や DataTableMapping

名を指定しなかった場合、DataAdapter は "Table" と謂う名前の DataTableMapping を検索する。其

の DataTableMapping が存在しない場合は、DataTable の TableName が "Table" に成る。"Table" と

謂う名前の DataTableMapping を作成する事で既定の DataTableMapping を指定出来る。

次に示すのは、System.Data.Common 名前空間から DataTableMapping を作成し、其れに "Table" と

謂う名前を付けて、指定した DataAdapter の既定のマップと仕て設定するコード例で有る。此の例で

は、其の後、クエリ結果の最初のテーブル(Northwind データベースの Customers テーブル)の列を

DataSet の Northwind Customers テーブルに有る、より解り易い名前のセットに割り当てる。割り当

てられない列には、データソースの列名が使用される。

Visual Basic

Dim mapping As DataTableMapping = _

adapter.TableMappings.Add("Table", "NorthwindCustomers")

mapping.ColumnMappings.Add("CompanyName", "Company")

mapping.ColumnMappings.Add("ContactName", "Contact")

mapping.ColumnMappings.Add("PostalCode", "ZIPCode")

adapter.Fill(custDS)

Page 12: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-12-

C#

DataTableMapping mapping =

adapter.TableMappings.Add("Table", "NorthwindCustomers");

mapping.ColumnMappings.Add("CompanyName", "Company");

mapping.ColumnMappings.Add("ContactName", "Contact");

mapping.ColumnMappings.Add("PostalCode", "ZIPCode");

adapter.Fill(custDS);

更に高度な条件下では、同じ DataAdapter を使用して複数の割り当てが設定された複数テーブルの読

み込みのサポートが必要な場合が有る。此の場合、DataTableMapping オブジェクトを追加する。

Fill メソッドに DataSet のインスタンスと DataTableMapping 名が渡された時、其の名前の割り当て

が存在する場合は其の名前が使用され、存在しない場合は其の名前の DataTable が使用される。

次 に 示 す の は 、 Customers と 謂 う 名 前 と BizTalkSchema と 謂 う DataTable 名 を 持 つ

DataTableMapping を作成する例で有る。此の例では、其の後で、SELECT ステートメントで返され

た行を BizTalkSchema DataTable に割り当てゝ居る。

Visual Basic

Dim mapping As ITableMapping = _

adapter.TableMappings.Add("Customers", "BizTalkSchema")

mapping.ColumnMappings.Add("CustomerID", "ClientID")

mapping.ColumnMappings.Add("CompanyName", "ClientName")

mapping.ColumnMappings.Add("ContactName", "Contact")

mapping.ColumnMappings.Add("PostalCode", "ZIP")

adapter.Fill(custDS, "Customers")

C#

ITableMapping mapping =

adapter.TableMappings.Add("Customers", "BizTalkSchema");

mapping.ColumnMappings.Add("CustomerID", "ClientID");

mapping.ColumnMappings.Add("CompanyName", "ClientName");

mapping.ColumnMappings.Add("ContactName", "Contact");

mapping.ColumnMappings.Add("PostalCode", "ZIP");

adapter.Fill(custDS, "Customers");

※ 列マップにソースの列名を指定しなかった場合、又は、テーブルマップにソーステーブル名を指定

しなかった場合は、自動的に既定の名前が生成される。列マップにソース列を指定しなかった場合

は、列マップに SourceColumn1 から始まるインクリメンタル既定名 SourceColumnN が割り当て

られる。テーブルマップにソーステーブル名を指定しなかった場合は、テーブルマップに

SourceTable1 から始まるインクリメンタル既定名 SourceTableN が割り当てられる。

※ 列マップには、SourceColumnN の命名規則を使用しない事、亦、テーブルの割り当てには

SourceTableN を使用しない事を推奨する。此れは、指定した名前が ColumnMappingCollection 内

の既存する既定の列マップ名、又は、DataTableMappingCollection 内のテーブルマップ名と競合し

ない様にする為で有る。指定した名前が既に存在する場合は、例外がスローされる。

Page 13: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-13-

複数結果セット

SelectCommand が複数のテーブルを返す場合、Fill は DataSet 内のテーブルに対するインクリメント

値を含むテーブル名を自動的に生成する。此れは、指定したテーブル名で開始し、TableNameN の形式

で TableName1 から数値を加算して行く名前に成る。自動的に生成されたテーブル名は、テーブルの割

り当てを使用して DataSet 内でテーブルに指定する名前に変換出来る。例えば、Customers と Orders

と謂う 2 つのテーブルを返す SelectCommand に対して、次の Fill 呼出を実行する。

Visual Basic、C#

adapter.Fill(customersDataSet, "Customers")

DataSet 内に Customers と Customers1 と謂う 2 つのテーブルが作成される。テーブルマップを使用

して、2 つ目のテーブルに Customers1 と謂う名前の代わりに Orders と謂う名前を付ける事が出来る。

其れには、次の例に示す様に、ソーステーブル Customers1 を DataSet テーブル Orders に割り当てる。

Visual Basic、C#

adapter.TableMappings.Add("Customers1", "Orders")

adapter.Fill(customersDataSet, "Customers")

■ DataAdapter に依るパラメータの使用

DataAdapter は、データソースからデータを取得したりデータソースのデータを更新したりする為に使

用される、次の 4 つのプロパティを持って居る。SelectCommand プロパティは、データソースからデ

ータを返す。InsertCommand、UpdateCommand、DeleteCommand の各プロパティは、データソー

スの変更を管理する為に使用する。SelectCommand プロパティは DataAdapter の Fill メソッドを呼

び出す前に設定する必要が有る。InsertCommand、UpdateCommand、DeleteCommand の各プロパ

ティは、DataAdapter の Update メソッドを呼び出す前に DataSet 内のデータに対して行われた変更

に基づいて設定する必要が有る。例えば、行が追加された場合、InsertCommand は Update 呼出の前

に設定されて居る必要が有る。Update が挿入行、更新行、又は、削除行を処理して居る時、DataAdapter

が夫々れの Command プロパティを使用して其のアクションを処理する。変更された行に付いての現在

の情報が Parameters コレクションを通じて Command オブジェクトに渡される。

データソースの行を更新する時は、一意識別子を使用してテーブル内の更新する列を識別する

UPDATE ステートメントを呼び出す。一意識別子は、一般には主キーフィールドの値で有る。UPDATE

ステートメントは、次の Transact-SQL ステートメントで示す様に、一意識別子、及び、更新する列と

値の両方を含むパラメータを使用する。

UPDATE Customers SET CompanyName = @CompanyName

WHERE CustomerID = @CustomerID

※ パラメータのプレースホルダの構文は、データソースに依って異なる。次に、SQL Server のデータ

ソースのプレースホルダの例を示す。System.Data.OleDb パラメータと System.Data.Odbc パラメ

ータのプレースホルダと仕て、疑問符( ? )を使用する。

此の Visual Basic の例では、CompanyName フィールドは、CustomerID が@CustomerID パラメータ

の値と等しい列の@CompanyName パラメータの値で更新される。此等のパラメータは SqlParameter

オブジェクトの SourceColumn プロパティを使用して、変更された行から情報を取得する。前のサンプ

ル UPDATE ステートメントのパラメータを次に示す。此のコードは、変数 adapter が有効な

SqlDataAdapter オブジェクトを表す事を前提と仕て居る。

Page 14: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-14-

Visual Basic

adapter.Parameters.Add( _

"@CompanyName", SqlDbType.NChar, 15, "CompanyName")

Dim parameter As SqlParameter = _

adapter.UpdateCommand.Parameters.Add("@CustomerID", _

SqlDbType.NChar, 5, "CustomerID")

parameter.SourceVersion = DataRowVersion.Original

Parameters コレクションの Add メソッドは、パラメータ名、DataAdapter 固有の型、サイズ(其の型

に適用可能な場合)、及び、DataTable の SourceColumn の名前を受け取る。@CustomerID パラメー

タの SourceVersion が Original に設定されて居る事に注意され度い。此の設定に依り、変更済みの

DataRow の中で、識別を行う 1 つ、又は、複数の列の値が既に変更されて居る場合に、データソース

内の既存の行を更新する事を保証出来る。識別列の値が既に変更されて居る場合は、Original 行の値は

データソースの現在の値と一致し、Current 行の値は更新済みの値を格納する。@CompanyName パラ

メータの SourceVersion は設定されて居ない為、既定の Current の行の値が使用される。

SqlClient の例

SQL ステートメントを SqlDataAdapter の SelectCommand、InsertCommand、UpdateCommand、

DeleteCommand の各プロパティの CommandText と仕て使用する例を次に示す。SqlDataAdapter オ

ブジェクトの場合は、名前付きのパラメータを使用する必要が有る。

Visual Basic

Dim selectSQL As String = _

"SELECT CustomerID, CompanyName FROM Customers " & _

"WHERE CountryRegion = @CountryRegion AND City = @City"

Dim insertSQL As String = _

"INSERT INTO Customers (CustomerID, CompanyName) " & _

"VALUES (@CustomerID, @CompanyName)"

Dim updateSQL As String = _

"UPDATE Customers SET CustomerID = @CustomerID, & _

"CompanyName = @CompanyName " & _

"WHERE CustomerID = @OldCustomerID"

Dim deleteSQL As String = _

"DELETE FROM Customers WHERE CustomerID = @CustomerID"

C#

string selectSQL =

"SELECT CustomerID, CompanyName FROM Customers WHERE CountryRegion = " +

"@CountryRegion AND City = @City";

string insertSQL = "INSERT INTO Customers (CustomerID, CompanyName) " +

"VALUES (@CustomerID, @CompanyName)";

string updateSQL = "UPDATE Customers SET CustomerID = @CustomerID, " +

"CompanyName = @CompanyName WHERE CustomerID = @OldCustomerID";

string deleteSQL =

"DELETE FROM Customers WHERE CustomerID = @CustomerID";

OleDb 又は Odbc の例

OleDbDataAdapter オブジェクトと OdbcDataAdapter オブジェクトの場合は、疑問符( ? )のプレ

ースホルダを使用してパラメータを識別する必要が有る。

Page 15: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-15-

Visual Basic

Dim selectSQL As String = _

"SELECT CustomerID, CompanyName FROM Customers " & _

"WHERE CountryRegion = ? AND City = ?"

Dim insertSQL AS String = _

"INSERT INTO Customers (CustomerID, CompanyName) VALUES (?, ?)"

Dim updateSQL AS String = _

"UPDATE Customers SET CustomerID = ?, CompanyName = ? " & _

WHERE CustomerID = ?"

Dim deleteSQL As String = "DELETE FROM Customers WHERE CustomerID = ?"

C#

string selectSQL =

"SELECT CustomerID, CompanyName FROM Customers " +

"WHERE CountryRegion = ? AND City = ?";

string insertSQL =

"INSERT INTO Customers (CustomerID, CompanyName) " +

"VALUES (?, ?)";

string updateSQL =

"UPDATE Customers SET CustomerID = ?, CompanyName = ? " +

"WHERE CustomerID = ? ";

string deleteSQL = "DELETE FROM Customers WHERE CustomerID = ?";

パラメータと仕て使用されるクエリステートメントは、作成する必要の有る入力パラメータと出力パラ

メータを定義する。パラメータを作成するには、Parameters.Add メソッド、又は、Parameter コンス

トラクタを使用して列名、データ型、及び、サイズを指定する。Integer 等組み込みのデータ型の場合

は、サイズを含める必要は無いし、其の場合は自動的に既定のサイズを指定する事に成る。

前例のSQLステートメントのパラメータを作成し、DataSetにデータを格納するコード例を次に示す。

SqlClient

Visual Basic

' 変数 connection は、有効な SqlConnection オブジェクトとする。

Dim adapter As SqlDataAdapter = New SqlDataAdapter

Dim selectCMD AS SqlCommand = New SqlCommand(selectSQL, connection)

adapter.SelectCommand = selectCMD

selectCMD.Parameters.Add("@CountryRegion", SqlDbType.NVarChar, 15).Value = "UK"

selectCMD.Parameters.Add("@City", SqlDbType.NVarChar, 15).Value = "London"

Dim customers As DataSet = New DataSet

adapter.Fill(customers, "Customers")

C#

// 変数 connection は、有効な SqlConnection オブジェクトとする。

SqlDataAdapter adapter = new SqlDataAdapter( );

SqlCommand selectCMD = new SqlCommand(selectSQL, connection);

adapter.SelectCommand = selectCMD;

selectCMD.Parameters.Add("@CountryRegion", SqlDbType.NVarChar, 15).Value = "UK";

selectCMD.Parameters.Add("@City", SqlDbType.NVarChar, 15).Value = "London";

DataSet customers = new DataSet( );

adapter.Fill(customers, "Customers");

Page 16: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-16-

OleDb

Visual Basic

' 変数 connection は、有効な OleDbConnection オブジェクトとする。

Dim adapter As OleDbDataAdapter = New OleDbDataAdapter

Dim selectCMD AS OleDbCommand = New OleDbCommand(selectSQL, connection)

adapter.SelectCommand = selectCMD selectCMD.Parameters.Add("@CountryRegion", OleDbType.VarChar, 15).Value = "UK"

selectCMD.Parameters.Add("@City", OleDbType.VarChar, 15).Value = "London" Dim customers As DataSet = New DataSet

adapter.Fill(customers, "Customers")

C#

// 変数 connection は、有効な OleDbConnection オブジェクトとする。

OleDbDataAdapter adapter = new OleDbDataAdapter( );

OleDbCommand selectCMD = new OleDbCommand(selectSQL, connection);

adapter.SelectCommand = selectCMD; selectCMD.Parameters.Add("@CountryRegion", OleDbType.VarChar, 15).Value = "UK";

selectCMD.Parameters.Add("@City", OleDbType.VarChar, 15).Value = "London"; DataSet customers = new DataSet( );

adapter.Fill(customers, "Customers");

Odbc

Visual Basic

' 変数 connection は、有効な OdbcConnection オブジェクトとする。

Dim adapter As OdbcDataAdapter = New OdbcDataAdapter

Dim selectCMD AS OdbcCommand = New OdbcCommand(selectSQL, connection)

adapter.SelectCommand = selectCMD selectCMD.Parameters.Add("@CountryRegion", OdbcType.VarChar, 15).Value = "UK"

selectCMD.Parameters.Add("@City", OdbcType.VarChar, 15).Value = "London" Dim customers As DataSet = New DataSet

adapter.Fill(customers, "Customers")

C#

// 変数 connection は、有効な OdbcConnection オブジェクトとする。

OdbcDataAdapter adapter = new OdbcDataAdapter( );

OdbcCommand selectCMD = new OdbcCommand(selectSQL, connection);

adapter.SelectCommand = selectCMD; selectCMD.Parameters.Add("@CountryRegion", OdbcType.VarChar, 15).Value = "UK";

selectCMD.Parameters.Add("@City", OdbcType.VarChar, 15).Value = "London"; DataSet customers = new DataSet( );

adapter.Fill(customers, "Customers");

※ 或るパラメータに対してパラメータ名がサポートされて居ない場合は、Parameter1 から増分して行

く既定名 ParameterN が割り当てられる。パラメータ名を指定する時には、ParameterN の名前付

けルールを使用しない事を推奨する。此れは、指定した名前が ParameterCollection 内の既存のパ

ラメータ名と競合しない様にする為で有る。指定した名前が既に存在する場合は、例外が発生する。

Page 17: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-17-

Parameter.DbType

パラメータの型は、.NET Framework データプロバイダに固有の属性で有る。型が指定されて居る場合

は、其の値がデータソースに渡される前に Parameter の値が.NET Framework データプロバイダ型に

変換される。Parameter オブジェクトの DbType プロパティを特定の DbType に設定する一般的な方法

で Parameter の型を指定する事も出来る。

Parameter オブジェクトの.NET Frameworkデータプロバイダ型は、ParameterオブジェクトのValue

の.NET Framework 型か、又は、Parameter オブジェクトの DbType から推論される。Parameter 値

と仕て渡されるオブジェクト、又は、指定された DbType に基づいて推論される Parameter 型を、次

の表に示す。

.NET Framework System.Data.DbType SqlDbType OleDbType OdbcType OracleType

bool Boolean Bit Boolean Bit Byte

byte Byte TinyInt UnsignedTinyInt TinyInt Byte

byte[] Binary VarBinary. ※2 VarBinary Binary Raw

char ※1 Char Char Byte

DateTime DateTime DateTime DBTimeStamp DateTime DateTime

Decimal Decimal Decimal Decimal Numeric Number

double Double Float Double Double Double

float Single Real Single Real Float

Guid Guid UniqueIdentifier Guid UniqueIdentifier Raw

Int16 Int16 SmallInt SmallInt SmallInt Int16

Int32 Int32 Int Int Int Int32

Int64 Int64 BitInt BigInt BigInt Number

object Object Variant Variant ※1 Blob

string String NVarChar ※3 VarWChar NVarChar NVarChar

TimeSpan Time ※1 DBTime Time DateTime

UInt16 UInt16 ※1 UnsignedSmallInt Int UInt16

UInt32 UInt32 ※1 UnsignedInt BigInt UInt32

UInt64 UInt64 ※1 UnsignedBigInt Numeric Number

AnsiString VarChar VarChar VarChar VarChar

AnsiStringFixedLength Char Char Char Char

Currency Money Currency ※1 Number

Date ※1 DBDate Date DateTime

SByte ※1 TinyInt ※1 SByte

StringFixedLength NChar WChar NChar NChar

Time ※1 DBTime Time DateTime

VarNumeric ※1 VarNumeric ※1 Number

※1 サポートされて居ない。

※2 バイト配列が VarBinary の最大サイズ(8000 バイト)より大きい場合は、此の暗黙の変換はエラ

ーに成る。8000 バイトを超えるバイト配列の場合は、明示的に SqlDbType を設定する。

※3 文字列が NVarChar の最大サイズ(4000 文字)より大きい場合、此の暗黙の変換はエラーに成る。

4000 文字を超える文字列の場合は、明示的に SqlDbType を設定する。

Page 18: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-18-

※ .NET Framework Version 1.0 に同梱されて居る.NET Framework データプロバイダは、Decimal

パラメータ値の Precision と Scale を確認しない為、切り捨てられたデータがデータソースに挿入さ

れる事が有る。.NET Framework Version 1.0 を使用して居る場合は、Decimal 値の Precision と

Scale を検証してからパラメータ値を設定する。.NET Framework Version 1.1 以降では、Decimal

パラメータ値で無効な Precision が設定されて居る場合、例外がスローされる。Decimal パラメータ

スケールを超える Scale 値は、切り捨てられる。

※ .NET Framework Version 1.0 以降では、System.Data.SqlTypes を System.Data.SqlClient と共に

使用出来る。詳細に付いては、「SqlTypes の使用」を参照され度い。

Parameter.Direction

Parameter の Direction を設定する為に ParameterDirection 列挙型で使用出来る値を次の表に示す。

メンバ名 説明

Input 此のパラメータは入力パラメータで有る(既定値)。

InputOutput 此のパラメータは入力と出力の両方の機能を持って居る。

Output 此のパラメータは出力パラメータで有る。

ReturnValue 此のパラメータは戻り値を表す。

Parameter の Direction を設定する方法を次のコード例に示す。

Visual Basic、C#

parameter.Direction = ParameterDirection.Output

Parameter.SourceColumn、Parameter.SourceVersion

SourceColumn と SourceVersion は、Parameter コンストラクタに記述子と仕て渡したり、既存の

Parameter のプロパティと仕て設定する事も出来る。SourceColumn は、Parameter の値の取得元で

有る DataRow の DataColumn の名前で有る。SourceVersion は、DataAdapter が値を取得する為に使

用する DataRow バージョンを指定する。

SourceVersion で使用出来る DataRowVersion 列挙型の値を次の表に示す。

メンバ名 説明

Current 此のパラメータは列の現在の値を使用する(既定値)。

Default 此のパラメータは列の DefaultValue を使用する。

Original 此のパラメータは列の元の値を使用する。

Proposed 此のパラメータは提示された値を使用する。

CustomerID 列を 2 つのパラメータ@CustomerID(SET CustomerID = @CustomerID)及び

@OldCustomerID(WHERE CustomerID = @OldCustomerID)の SourceColumn と仕て使用する

UPDATE ステートメントを定義するコード例を次に示す。@CustomerID パラメータは CustomerID

列を DataRow の現在の値に更新する為に使用されて居る。其の為、SourceVersion を Current に設定

した CustomerID SourceColumn が使用されて居る。@OldCustomerID パラメータは、データソース

の現在の行を識別する為に使用されて居る。其の行の Original バージョンで一致する列の値が見付かっ

た為、SourceVersion を Original に設定した同じ SourceColumn(CustomerID)が使用されて居る。

Page 19: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-19-

SqlClient

Visual Basic

adapter.UpdateCommand.Parameters.Add( _

"@CustomerID", SqlDbType.NChar, 5, "CustomerID")

adapter.UpdateCommand.Parameters.Add( _

"@CompanyName", SqlDbType.NVarChar, 40, "CompanyName")

Dim parameter As SqlParameter = _

adapter.UpdateCommand.Parameters.Add("@OldCustomerID", _

SqlDbType.NChar, 5, "CustomerID")

parameter.SourceVersion = DataRowVersion.Original

C#

adapter.UpdateCommand.Parameters.Add(

"@CustomerID", SqlDbType.NChar, 5, "CustomerID");

adapter.UpdateCommand.Parameters.Add(

"@CompanyName", SqlDbType.NVarChar, 40, "CompanyName");

SqlParameter parameter =

adapter.UpdateCommand.Parameters.Add(

"@OldCustomerID", SqlDbType.NChar, 5, "CustomerID");

parameter.SourceVersion = DataRowVersion.Original;

UpdatedRowSource

Command オブジェクトの UpdatedRowSource プロパティを使用すると、データソースから返された

値を DataSet に割り当てる方法を制御出来る。UpdatedRowSource プロパティを UpdateRowSource

列挙型の値の 1 つに設定する事で、DataAdapter コマンドが返したパラメータを無視するか、DataSet

内の変更行に適用するかを制御出来る。最初に返された行(存在する場合)を、DataSet 内の変更行に

適用するか何うかを指定する事も出来る。

UpdateRowSource 列挙型の様々の値と、其等の値が DataAdapter で使用されるコマンドの動作に何の

様に影響するかを次の表で説明する。

UpdateRowSource 説明

Both 出力パラメータと返された結果セットの最初の行を DataSet 内の変更行に割り当てる。

FirstReturnedRecord 返された結果セットの最初の行のデータ丈を DataSet 内の変更行に割り当てる。

None 出力パラメータ又は返された結果セットの行が無視される。

OutputParameters 出力パラメータ丈を DataSet 内の変更行に割り当てる。

■ パラメータと戻り値の指定

ストアドプロシージャには、入力パラメータと出力パラメータの他に戻り値を含める事が出来る。次の

例は、ADO.NET が新しいレコードを、主キー列が SQL Server データベースの ID 列と成って居るテ

ーブルに挿入して、入力パラメータ、出力パラメータ、及び、戻り値を受け渡す例を説明して居る。

此の例では、次のストアドプロシージャを使用して Northwind Categories テーブルに新しいカテゴリ

を挿入する。此のストアドプロシージャは、CategoryName 列の値を入力パラメータと仕て受け取り、

SCOPE_IDENTITY 関数を使用して ID フィールド CategoryID の新しい値を取得し、其の値を出力パ

Page 20: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-20-

ラメータ内に返す。RETURN ステートメントは、@@ROWCOUNT 関数を使用して、挿入された行の

数を返す。

CREATE PROCEDURE InsertCategory

@CategoryName nchar(15),

@Identity int OUTPUT

AS

SET NOCOUNT ON

INSERT INTO Categories (CategoryName) VALUES(@CategoryName)

SET @Identity = SCOPE_IDENTITY( )

RETURN @@ROWCOUNT

上述の InsertCategory ストアドプロシージャを SqlDataAdapter の InsertCommand のソースと仕て

使用する例を次に示す。@Idenrity 出力パラメータと戻り値は、SqlDataAdapter の Update メソッド

が呼び出され、データベースにレコードが挿入された後で DataSet に反映される。

※ OleDbDataAdapter を使用する場合、ReturnValue の ParameterDirection を含むパラメータを他

のパラメータより先に指定する必要が有る。

Visual Basic

' 変数 connection は、有効な SqlConnection オブジェクトとする。

Dim adapter As SqlDataAdapter = New SqlDataAdapter( _

"SELECT CategoryID, CategoryName FROM dbo.Categories", connection)

adapter.InsertCommand = New SqlCommand( _

"InsertCategory" , connection)

adapter.InsertCommand.CommandType = CommandType.StoredProcedure

Dim parameter As SqlParameter = adapter.InsertCommand.Parameters.Add( _

"@RowCount", SqlDbType.Int)

parameter.Direction = ParameterDirection.ReturnValue

adapter.InsertCommand.Parameters.Add( _

"@CategoryName", SqlDbType.NChar, 15, "CategoryName")

parameter = adapter.InsertCommand.Parameters.Add( _

"@Identity", SqlDbType.Int, 0, "CategoryID")

parameter.Direction = ParameterDirection.Output

Dim categoriesDS As DataSet = New DataSet( )

adapter.Fill(categoriesDS, "Categories")

Dim newRow As DataRow = categoriesDS.Tables("Categories").NewRow( )

newRow("CategoryName") = "New Category"

categoriesDS.Tables("Categories").Rows.Add(newRow)

adapter.Update(categoriesDS, "Categories")

Dim rowCount As Int32 = _

CInt(adapter.InsertCommand.Parameters("@RowCount").Value)

Page 21: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-21-

C#

// 変数 connection は、有効な SqlConnection オブジェクトとする。

SqlDataAdapter adapter = new SqlDataAdapter(

"SELECT CategoryID, CategoryName FROM dbo.Categories", connection);

adapter.InsertCommand = new SqlCommand("InsertCategory", connection);

adapter.InsertCommand.CommandType = CommandType.StoredProcedure;

SqlParameter parameter = adapter.InsertCommand.Parameters.Add(

"@RowCount", SqlDbType.Int);

parameter.Direction = ParameterDirection.ReturnValue;

adapter.InsertCommand.Parameters.Add(

"@CategoryName", SqlDbType.NChar, 15, "CategoryName");

parameter = adapter.InsertCommand.Parameters.Add(

"@Identity", SqlDbType.Int, 0, "CategoryID");

parameter.Direction = ParameterDirection.Output;

DataSet categoriesDS = new DataSet( );

adapter.Fill(categoriesDS, "Categories");

DataRow newRow = categoriesDS.Tables["Categories"].NewRow( );

newRow["CategoryName"] = "New Category";

categoriesDS.Tables["Categories"].Rows.Add(newRow);

adapter.Update(categoriesDS, "Categories");

Int32 rowCount = (Int32)adapter.InsertCommand.Parameters["@RowCount"].Value;

■ DataAdapter に依るバッチ更新の実行

以前のバージョンの ADO.NET では、DataSet に格納されて居る変更内容をデータベースに反映する場

合、DataAdapter の Update メソッドを実行して、1 行宛データベースを更新して居た。此のメソッド

は、指定された DataTable 内の行を反復処理すると、各 DataRow を調べ、行が変更された事を確認す

る。行が変更されて居る場合、其の行の RowState プロパティの値に基づいて、適切な UpdateCommand、

InsertCommand、又は、DeleteCommand の孰れかを呼び出す。各行の更新では、データベースへの

ネットワークラウンドトリップが発生する。

ADO.NET 2.0 では、UpdateBatchSize プロパティが DataAdapter に依って公開される。

UpdateBatchSize を正の整数値に設定すると、データベースの更新が指定されたサイズのバッチと仕て

送信される。例えば、UpdateBatchSize を 10 に設定すると、10 個の個別のステートメントがグループ

化され、単一のバッチと仕て送信される。UpdateBatchSize を 0 に設定すると、DataAdapter は、サ

ーバーが処理出来る最大のバッチサイズを使用する。1 に設定すると、バッチ更新が無効に成り、1 行

宛送信される。

サイズの大きいバッチを実行すると、パフォーマンスが低下する可能性が有る。其の為、アプリケーシ

ョンを実装する前に、バッチの最適なサイズ設定をテストする必要が有る。

UpdateBatchSize プロパティの使用

バッチ更新を有効にする場合、DataAdapter の UpdateCommand、InsertCommand、DeleteCommand

Page 22: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-22-

の UpdatedRowSource プロパティ値を、None か OutputParameters に設定する必要が有る。バッチ

更新を実行する際、FirstReturnedRecord 又は Both のコマンドの UpdatedRowSource プロパティ値

は、無効に成る。

UpdateBatchSize プロパティを使用するプロシージャを次に示す。此のプロシージャは、2 つの引数を

取る。1 つは、Production.ProductCategory テーブル内の ProductCategoryID フィールドと Name フ

ィールドを表す列を持つ DataSet オブジェクトで、今 1 つは、バッチサイズ(バッチファイル内の行数)

を表す整数で有る。此のコードに依り、新しい SqlDataAdapter オブジェクトが作成され、其の

UpdateCommand プロパティ、InsertCommand プロパティ、DeleteCommand プロパティが設定され

る。此のコードは、DataSet オブジェクトに依って行が変更済みに成って居る事を前提と仕て居る。此

のオブジェクトは、UpdateBatchSize プロパティを設定し、更新を実行する。

Visual Basic

Public Sub BatchUpdate(ByVal dataTable As DataTable, ByVal batchSize As Int32)

' 関数 GetConnectionString が、有効な接続文字列を返す物とする。

Dim connectionString As String = GetConnectionString( )

' AdventureWorks データベースに接続

Using connection As New SqlConnection(connectionString)

' SqlDataAdapter の生成

Dim adapter As New SqlDataAdapter( )

' UPDATE コマンドとパラメータの設定

adapter.UpdateCommand = New SqlCommand( _

"UPDATE Production.ProductCategory SET " & _

"Name=@Name WHERE ProductCategoryID=@ProdCatID;", connection)

adapter.UpdateCommand.Parameters.Add("@Name", _

SqlDbType.NVarChar, 50, "Name")

adapter.UpdateCommand.Parameters.Add("@ProdCatID", _

SqlDbType.Int, 4, " ProductCategoryID ")

adapter.UpdateCommand.UpdatedRowSource = _

UpdateRowSource.None

' INSERT コマンドとパラメータの設定

adapter.InsertCommand = New SqlCommand( _

"INSERT INTO Production.ProductCategory (Name) VALUES (@Name);", connection)

adapter.InsertCommand.Parameters.Add("@Name", _

SqlDbType.NVarChar, 50, "Name")

adapter.InsertCommand.UpdatedRowSource = _

UpdateRowSource.None

' DELETE コマンドとパラメータの設定

adapter.DeleteCommand = New SqlCommand( _

"DELETE FROM Production.ProductCategory " & _

"WHERE ProductCategoryID=@ProdCatID;", connection)

adapter.DeleteCommand.Parameters.Add("@ProdCatID", _

SqlDbType.Int, 4, " ProductCategoryID ")

adapter.DeleteCommand.UpdatedRowSource = UpdateRowSource.None

' バッチサイズの設定

adapter.UpdateBatchSize = batchSize

' Update の実行

adapter.Update(dataTable)

End Using

End Sub

Page 23: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-23-

C#

public static void BatchUpdate(DataTable dataTable,Int32 batchSize)

{

// 関数 GetConnectionString が、有効な接続文字列を返す物とする。

string connectionString = GetConnectionString( );

// AdventureWorks データベースに接続

using (SqlConnection connection = new SqlConnection(connectionString))

{

// SqlDataAdapter の生成

SqlDataAdapter adapter = new SqlDataAdapter( );

// UPDATE コマンドとパラメータの設定

adapter.UpdateCommand = new SqlCommand(

"UPDATE Production.ProductCategory SET "

+ "Name=@Name WHERE ProductCategoryID=@ProdCatID;", connection);

adapter.UpdateCommand.Parameters.Add("@Name",

SqlDbType.NVarChar, 50, "Name");

adapter.UpdateCommand.Parameters.Add("@ProdCatID",

SqlDbType.Int, 4, "ProductCategoryID");

adapter.UpdateCommand.UpdatedRowSource = UpdateRowSource.None;

// INSERT コマンドとパラメータの設定

adapter.InsertCommand = new SqlCommand(

"INSERT INTO Production.ProductCategory (Name) VALUES (@Name);", connection);

adapter.InsertCommand.Parameters.Add("@Name",

SqlDbType.NVarChar, 50, "Name");

adapter.InsertCommand.UpdatedRowSource = UpdateRowSource.None;

// DELETE コマンドとパラメータの設定

adapter.DeleteCommand = new SqlCommand(

"DELETE FROM Production.ProductCategory "

+ "WHERE ProductCategoryID=@ProdCatID;", connection);

adapter.DeleteCommand.Parameters.Add("@ProdCatID",

SqlDbType.Int, 4, "ProductCategoryID");

adapter.DeleteCommand.UpdatedRowSource = UpdateRowSource.None;

// バッチサイズの設定

adapter.UpdateBatchSize = batchSize;

// Update の実行

adapter.Update(dataTable);

}

}

■ DataAdapter に依るデータソースの更新

DataAdapter の Update メソッドを呼び出して、変更を DataSet からデータソースに反映する。Update

メソッドは、Fillメソッドと同様に、引数と仕てDataSetのインスタンス、及び、オプションのDataTable

オブジェクト、又は、DataTable 名を受け取る。DataSet のインスタンスは、行われた変更点を格納す

る DataSet で有る。DataTable は、変更点の取得元のテーブルで有る。

Update メソッドを呼び出すと、DataAdapter は、既に加えられた変更を解析し、適切なコマンド

(INSERT、UPDATE、又は、DELETE)を実行する。DataAdapter は、DataRow へ加えられた変更

を検出すると、InsertCommand、UpdateCommand、又は、DeleteCommand を使用して其の変更を

Page 24: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-24-

処理する。其の結果、デザイン時にコマンド構文を指定し、可能な場合はストアドプロシージャを使用

する事に依り、ADO.NET アプリケーションのパフォーマンスを最適化出来る。コマンドは Update を

呼び出す前に明示的に設定する必要が有る。Update を呼び出し、其の更新に関連する適切なコマンド

が存在しない場合(例えば、削除済みの行に関連する DeleteCommand が存在しない場合)は、例外が

スローされる。

Command パラメータを使用して、DataSet 内の各変更行に対する SQL ステートメントやストアドプ

ロシージャに入力値と出力値を指定出来る。詳細に付いては、「DataAdapter に依るパラメータの使用」

を参照され度い。

DataTable を単一データベーステーブルに割り当てたり、単一データベースから生成する場合は、

DbCommandBuilder オブジェクトを利用して自動的に DataAdapter の DeleteCommand、

InsertCommand、及び、UpdateCommand を生成出来る。詳細に付いては、「コマンドの自動生成」

を参照され度い。

Update メソッドは変更点を元のデータソースに反映させるが、DataSet に最後にデータを格納した後、

他のクライアントがデータソースのデータを変更した可能性も有る。DataSet を現在のデータで更新す

るには、DataAdapter、及び、Fill メソッドを使用する。新しい行がテーブルに追加され、更新された

情報が既存の行に取り込まれる。Fill メソッドは、DataSet の行と SelectCommand に依って返された

行の主キーの値を調べて、新しい行が追加されたか、又は、既存の行が更新されたかを判断する。Fill

メソッドは、SelectCommand に依って返された結果の行に一致する主キーの値を持つ DataSet の行を

見付けた場合、SelectCommand に依って返された行の情報で既存の行を更新して、既存の行の

RowState を Unchanged に設定する。SelectCommand に依って返された行の主キーの値が、DataSet

の何の行の主キーの値にも一致しない場合、Fill メソッドは、RowState が Unchanged の新しい行を追

加する。

※ SelectCommand が OUTER JOIN の結果を返す場合、DataAdapter は、生成される DataTable に

PrimaryKey 値を設定しない。自分で PrimaryKey を定義して、重複行が正しく反映される様にす

る必要が有る。詳細に付いては、「テーブルの主キーの定義」を参照され度い。

Update メソッド呼出時に発生する例外を処理するには、行更新エラーが発生した時に RowUpdated イ

ベントを使用して応答するか(「DataAdapter イベントの使用」を参照)、又は、Update メソッド呼出

の前に DataAdapter.ContinueUpdateOnError を true に設定し、更新が完了した時点で特定の行の

RowError プロパティに格納されて居るエラー情報に応答する(「行のエラー情報の追加と読み取り」を

参照)。

※ DataSet、DataTable、又は、DataRow に対して AcceptChanges を呼び出すと、DataRow の総て

の Original 値が DataRow の Current 値で上書きされる。行を一意に識別するフィールド値が変更

された場合は、AcceptChanges 呼出の後、Original 値はデータソースの値と一致しなく成る。

DataAdapter の UpdateCommand を明示的に設定して、変更済みの行に対して更新を実行する方法を

次の例に示す。UPDATE ステートメントの WHERE 句に指定したパラメータが SourceColumn の

Original値を使用する様に設定されて居る事に注意され度い。Current値が既に変更されて居る可能性、

然して、データソースの値と一致して居ない可能性が有る為、此の設定は重要で有る。Original 値は、

データソースから DataTable にデータを取得する為に使用された値で有る。

Visual Basic

' 変数 connection は、有効な SqlConnection オブジェクトとする。

Dim adapter As SqlDataAdapter = New SqlDataAdapter( _

"SELECT CategoryID, CategoryName FROM Categories", connection)

Page 25: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-25-

adapter.UpdateCommand = New SqlCommand( _

"UPDATE Categories SET CategoryName = @CategoryName " & _

"WHERE CategoryID = @CategoryID", connection)

adapter.UpdateCommand.Parameters.Add( _

"@CategoryName", SqlDbType.NVarChar, 15, "CategoryName")

Dim parameter As SqlParameter = adapter.UpdateCommand.Parameters.Add( _

"@CategoryID", SqlDbType.Int)

parameter.SourceColumn = "CategoryID"

parameter.SourceVersion = DataRowVersion.Original

Dim dataSet As DataSet = New DataSet

adapter.Fill(dataSet, "Categories")

Dim row As DataRow = dataSet.Tables("Categories").Rows(0)

row("CategoryName") = "New Category"

adapter.Update(dataSet, "Categories")

C#

// 変数 connection は、有効な SqlConnection オブジェクトとする。

SqlDataAdapter dataAdpater = new SqlDataAdapter(

"SELECT CategoryID, CategoryName FROM Categories", connection);

dataAdpater.UpdateCommand = new SqlCommand(

"UPDATE Categories SET CategoryName = @CategoryName " +

"WHERE CategoryID = @CategoryID" , connection);

dataAdpater.UpdateCommand.Parameters.Add(

"@CategoryName", SqlDbType.NVarChar, 15, "CategoryName");

SqlParameter parameter = dataAdpater.UpdateCommand.Parameters.Add(

"@CategoryID", SqlDbType.Int);

parameter.SourceColumn = "CategoryID";

parameter.SourceVersion = DataRowVersion.Original;

DataSet dataSet = new DataSet( );

dataAdpater.Fill(dataSet, "Categories");

DataRow row = dataSet.Tables["Categories"].Rows[0];

row ["CategoryName"] = "New Category";

dataAdpater.Update(dataSet, "Categories");

AutoIncrement 列

データソースからのテーブルに自動インクリメント列が有る場合、DataSet の列に値を格納するには、

自動インクリメント値をストアドプロシージャの出力パラメータと仕て返して其れをテーブルの列に

割り当てるか、又は、DataAdapter の RowUpdated イベントを使用する。此の例に付いては、「ID 値

と Autonumber 値の取得」を参照され度い。

但し、DataSet 内の値はデータソースの値と同期しなく成る為、予期しない動作を発生する場合が有る。

例えば、CustomerID と謂う自動インクリメント主キー列を持つテーブルが有るとする。DataSet 内に

Page 26: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-26-

新しい2人の顧客を追加した場合、此等の顧客は自動インクリメントCustomerId値1と2を受け取る。

2 行目の顧客行が DataAdapter の Update メソッドに渡されると、新しく追加された行は、データソー

スで自動インクリメント CustomerID 値 1 を受け取る。此れは DataSet 内の値 2 とは一致しない。

DataAdapter が DataSet の行に戻り値を設定すると、1 行目の顧客行が既に CustomerID 値と仕て 1

を持つ為制約違反が発生する。

此の様な動作を防ぐには、データソースの自動インクリメント列と DataSet の自動インクリメント値を

使用する時に、AutoIncrementStep を-1、AutoIncrementSeed を 0 に設定して DataSet に列を作成し、

同時に 1 から始まり正のステップ値でインクリメントする自動インクリメント ID 値がデータソースで

生成される様にする。其の結果、DataSet では自動インクリメント値と仕て負の数値が生成される為、

データソースで生成される正の自動インクリメント値と矛盾しなく成る。今 1 つの方法は、自動インク

リメント列の代わりに Guid 型の列を使用する事で有る。Guid 値生成のアルゴリズムでは、データソー

スで生成される Guid と同じ Guid が DataSet で生成される事は無い。DataTable の列を定義する方法

の詳細に付いては、「DataTable のスキーマの定義」を参照され度い。

挿入、更新、削除の順序

通常の条件下では、DataSet を使用して行う変更の順序をデータソースに送信する事が重要で有る。例

えば、既存の行の主キーの値を更新し、其の新しい主キーの値で新しい行を追加する場合、更新は挿入

の前に処理する必要が有る。

DataTable の Select メソッドを使用すると、特定の RowState を持つ行丈を参照する DataRow 配列を

返す事が出来る。其の後で、返された DataRow 配列を DataAdapter の Update メソッドに渡して変更

行を処理出来る。更新する行のサブセットを指定する事で、挿入、更新、削除の処理順序を制御出来る。

例えば次のコードでは、テーブルの削除行を最初に処理し、次に更新行、最後に挿入行を処理する。

Visual Basic

Dim table As DataTable = dataSet.Tables("Customers")

' 最初に削除を処理

dataSet.Update(table.Select(Nothing, Nothing, _

DataViewRowState.Deleted))

' 次に更新を処理

adapter.Update(table.Select(Nothing, Nothing, _

DataViewRowState.ModifiedCurrent))

' 最後に挿入を処理

dataAdpater.Update(table.Select(Nothing, Nothing, _

DataViewRowState.Added))

C#

DataTable table = dataSet.Tables["Customers"];

// 最初に削除を処理

adapter.Update(table.Select(null, null, DataViewRowState.Deleted));

// 次に更新を処理

adapter.Update(table.Select(null, null,

DataViewRowState.ModifiedCurrent));

// 最後に挿入を処理

adapter.Update(table.Select(null, null, DataViewRowState.Added));

Page 27: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-27-

■ DataAdapter に依る UDT 列の更新

ユーザー定義型(UDT)に依り、DataSet と SqlDataAdapter を使用したデータの取得や変更がサポー

トされて居る。

データセットの読み込み

Transact-SQL の SELECT ステートメントを使用する事で、UDT 列の値を選択してデータアダプタを

使用し、データセットを読み込む事が出来る。次の例では、以下に示す構造とサンプル データで Points

テーブルが定義されて居る事を前提と仕て居る。次の Transact-SQL ステートメントに依って Points

テーブルが作成され、数行が挿入される。

CREATE TABLE dbo.Points (id int PRIMARY Key, p Point);

INSERT INTO dbo.Points VALUES (1, CONVERT(Point, '1,3'));

INSERT INTO dbo.Points VALUES (2, CONVERT(Point, '2,4'));

INSERT INTO dbo.Points VALUES (3, CONVERT(Point, '3,5'));

INSERT INTO dbo.Points VALUES (4, CONVERT(Point, '4,6'));

GO

次に示す ADO.NET コードでは、有効な接続文字列が取得され、新しい SqlDataAdapter が作成され、

Points テーブルからのデータ行が DataTable に読み込まれる。

Visual Basic

Dim da As New SqlDataAdapter( _

"SELECT id, p FROM dbo.Points", connectionString)

Dim datTable As New DataTable("Points")

da.Fill(datTable)

C#

SqlDataAdapter da = new SqlDataAdapter(

"SELECT id, p FROM dbo.Points", connectionString);

DataTable datTable = new DataTable("Points");

da.Fill(datTable);

データセットでの UDT データの更新

DataSet で UDT 列を更新するには、次の 2 つの方法が有る。

・SqlDataAdapter オブジェクトに、カスタムの InsertCommand、UpdateCommand、DeleteCommand

オブジェクトを指定する。

・コマンドビルダ(SqlCommandBuilder)を使用する事で、INSERT、UPDATE、DELETE コマン

ドが自動的に作成される。競合を検出するには、timestamp 列(エイリアス rowversion)を UDT を

含む SQL Server テーブルに追加する。timestamp データ型は、テーブル内の行のバージョンスタン

プ用に使用され、データベース内で一意で有る事が保証される。此れに依って、テーブル内の各行に

付いて自動的に生成された 8 バイトの 2 進数が公開され、テーブル内の値が変更された場合に自動的

に更新される。

但し SqlCommandBuilder では、基に成るテーブル内に timestamp 列が存在しない限り、競合の検

出に付いて UDT は考慮されない。各 UDT が同等でない事が有る為、コマンドの生成に "compare

original values" オプションが使用される場合は、WHERE 句には UDT が含まれない。

Page 28: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-28-

次の例では、Point UDT 列と timestamp 列を含む、2 番目のテーブルを作成する必要が有る。両方の

テーブルを使用して、カスタムコマンドオブジェクトを作成してデータを更新する方法と、timestamp

列を使用して更新する方法を示す。次の Transact-SQL ステートメントを実行して 2 番目のテーブルを

作成し、サンプルデータを読み込む。

CREATE TABLE dbo.Points_ts (id int PRIMARY KEY, p Point, ts timestamp);

INSERT INTO dbo.Points_ts (id, p) VALUES (1, CONVERT(Point, '1,3'));

INSERT INTO dbo.Points_ts (id, p) VALUES (2, CONVERT(Point, '2,4'));

INSERT INTO dbo.Points_ts (id, p) VALUES (3, CONVERT(Point, '3,5'));

INSERT INTO dbo.Points_ts (id, p) VALUES (4, CONVERT(Point, '4,6'));

次の ADO.NET の例には 2 つのメソッドが有る。

・UserProvidedCommands は、InsertCommand、UpdateCommand、DeleteCommand オブジェク

トを作成して、Points テーブル(timestamp 列を含まない)の Point UDT を更新する方法を示す。

・CommandBuilder は、timestamp 列を含む Points_ts テーブルで SqlCommandBuilder を使用する

方法を示す。

Visual Basic

Imports System

Imports System.Data

Imports System.Data.SqlClient

Module Module1

' 接続文字列の取得

Private connString As String = GetConnectionString( )

Sub Main( )

UserProvidedCommands( )

CommandBuilder( )

End Sub

Private Sub UserProvidedCommands( )

' 新しい SqlDataAdapter の生成

Dim da As New SqlDataAdapter( "SELECT id, p FROM dbo.Points", connString)

' INSERT/UPDATE/DELETE コマンドの設定

Dim idParam As SqlParameter

Dim pointParam As SqlParameter

da.InsertCommand = New SqlCommand( _

"INSERT INTO dbo.Points (id, p) VALUES (@id, @p)", da.SelectCommand.Connection)

idParam = da.InsertCommand.Parameters.Add("@id", SqlDbType.Int)

idParam.SourceColumn = "id"

pointParam = da.InsertCommand.Parameters.Add("@p", SqlDbType.Udt)

pointParam.SourceColumn = "p"

pointParam.UdtTypeName = "dbo.Point"

da.UpdateCommand = New SqlCommand( _

"UPDATE dbo.Points SET p = @p WHERE id = @id", da.SelectCommand.Connection)

idParam = da.UpdateCommand.Parameters.Add("@id", SqlDbType.Int)

Page 29: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-29-

idParam.SourceColumn = "id"

pointParam = da.UpdateCommand.Parameters.Add("@p", SqlDbType.Udt)

pointParam.SourceColumn = "p"

pointParam.UdtTypeName = "dbo.Point"

da.DeleteCommand = New SqlCommand( _

"DELETE dbo.Points WHERE id = @id", da.SelectCommand.Connection)

idParam = da.DeleteCommand.Parameters.Add("@id", SqlDbType.Int)

idParam.SourceColumn = "id"

' DataTable に UDT rows を格納

Dim datTable As New DataTable("Points")

da.Fill(datTable)

' Point 列の内容の表示

Dim r As DataRow

For Each r In datTable.Rows

Dim p As Point = CType(r(1), Point)

Console.WriteLine("ID: {0}, x={1}, y={1}", r(0), p.X, p.Y)

Next r

' 行の更新(DataTable に行が有る場合)

If datTable.Rows.Count > 0 Then

Dim oldPoint As Point = CType(datTable.Rows(0)(1), Point)

datTable.Rows(0)(1) = New Point(oldPoint.X + 1, oldPoint.Y + 1)

End If

' 最終行の削除(行が有る場合)

If datTable.Rows.Count > 0 Then

datTable.Rows(1).Delete( )

End If

' 行の挿入(100 は主キーの値なので、2 度実行すると失敗する)

datTable.Rows.Add(100, New Point(100, 200))

' データベースに変更を書き戻し

da.Update(datTable)

End Sub

Private Sub CommandBuilder( )

' 新しい SqlDataAdapter の生成

Dim da As New SqlDataAdapter( "SELECT id, ts, p FROM dbo.Points_ts", connString)

' データベースから UDT で行を抽出

Dim datTable As New DataTable("Points")

da.Fill(datTable)

' Point 列の内容の表示

Dim r As DataRow

For Each r In datTable.Rows

Dim p As Point = CType(r(2), Point)

Console.WriteLine("ID: {0}, x={1}, y={1}", r(0), p.X, p.Y)

Next r

Page 30: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-30-

' 行の更新(DataTable に行が有る場合)

If datTable.Rows.Count > 0 Then

Dim oldPoint As Point = CType(datTable.Rows(0)(2), Point)

datTable.Rows(0)(2) = New Point(oldPoint.X + 1, oldPoint.Y + 1)

End If

' 最終行の削除(行が有る場合)

If datTable.Rows.Count > 0 Then

datTable.Rows(1).Delete( )

End If

' 行の挿入(100 は主キーの値なので、2 度実行すると失敗する)

datTable.Rows.Add(100, Nothing, New Point(100, 200))

' CommandBuilder を使用して DML ステートメントの作成

Dim bld As New SqlCommandBuilder(da)

bld.ConflictDetection = ConflictOptions.CompareRowVersion

' データベースに変更を書き戻し

da.Update(datTable)

End Sub

Private Function GetConnectionString( ) As String

' 設定ファイルより接続文字列の取得

Return "Data Source=(local);Initial Catalog=AdventureWorks; Integrated Security=SSPI"

End Function

End Module

C#

using System;

using System.Data;

using System.Data.SqlClient;

class Class1

{

// 接続文字列の取得

private string connString = GetConnectionString( );

static void Main( )

{

UserProvidedCommands( );

CommandBuilder( );

}

static void UserProvidedCommands( )

{

// 新しい SqlDataAdapter の生成

SqlDataAdapter da = new SqlDataAdapter("SELECT id, p FROM dbo.Points", connString);

// INSERT/UPDATE/DELETE コマンドの設定

SqlParameter idParam;

SqlParameter pointParam;

da.InsertCommand = new SqlCommand(

"INSERT INTO dbo.Points (id, p) VALUES (@id, @p)",

da.SelectCommand.Connection);

Page 31: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-31-

idParam = da.InsertCommand.Parameters.Add("@id", SqlDbType.Int);

idParam.SourceColumn = "id";

pointParam = da.InsertCommand.Parameters.Add("@p", SqlDbType.Udt);

pointParam.SourceColumn = "p";

pointParam.UdtTypeName = "dbo.Point";

da.UpdateCommand = new SqlCommand(

"UPDATE dbo.Points SET p = @p WHERE id = @id", da.SelectCommand.Connection);

idParam = da.UpdateCommand.Parameters.Add("@id", SqlDbType.Int);

idParam.SourceColumn = "id";

pointParam = da.UpdateCommand.Parameters.Add("@p", SqlDbType.Udt);

pointParam.SourceColumn = "p";

pointParam.UdtTypeName = "dbo.Point";

da.DeleteCommand = new SqlCommand(

"DELETE dbo.Points WHERE id = @id", da.SelectCommand.Connection);

idParam = da.DeleteCommand.Parameters.Add("@id", SqlDbType.Int);

idParam.SourceColumn = "id";

// DataTable に UDT rows を格納

DataTable datTable = new DataTable("Points");

da.Fill(datTable);

// Point 列の内容の表示

foreach(DataRow r in datTable.Rows)

{

Point p = (Point)r[1];

Console.WriteLine("ID: {0}, x={1}, y={1}", r[0], p.X, p.Y);

}

// 行の更新(DataTable に行が有る場合)

if(datTable.Rows.Count > 0 )

{

Point oldPoint = (Point)datTable.Rows[0][1];

datTable.Rows[0][1] = new Point(oldPoint.X+1, oldPoint.Y+1);

}

// 最終行の削除(行が有る場合)

if(datTable.Rows.Count > 0 )

{

datTable.Rows[1].Delete( );

}

// 行の挿入(100 は主キーの値なので、2 度実行すると失敗する)

datTable.Rows.Add(100, new Point(100, 200));

// データベースに変更を書き戻し

da.Update(datTable);

}

static void CommandBuilder( )

{

// 新しい SqlDataAdapter の生成

SqlDataAdapter da = new SqlDataAdapter(

Page 32: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-32-

"SELECT id, ts, p FROM dbo.Points_ts", connString);

// データベースから UDT で行を抽出

DataTable datTable = new DataTable("Points");

da.Fill(datTable);

// Point 列の内容の表示

foreach (DataRow r in datTable.Rows)

{

Point p = (Point)r[2];

Console.WriteLine("ID: {0}, x={1}, y={1}", r[0], p.X, p.Y);

}

// 行の更新(DataTable に行が有る場合)

if (datTable.Rows.Count > 0)

{

Point oldPoint = (Point)datTable.Rows[0][2];

datTable.Rows[0][2] = new Point(oldPoint.X + 1, oldPoint.Y + 1);

}

// 最終行の削除(行が有る場合)

if (datTable.Rows.Count > 0)

{

datTable.Rows[1].Delete( );

}

// 行の挿入(100 は主キーの値なので、2 度実行すると失敗する)

datTable.Rows.Add(100, null, new Point(100, 200));

// CommandBuilder を使用して DML ステートメントの作成

SqlCommandBuilder bld = new SqlCommandBuilder(da);

bld.ConflictDetection = ConflictOptions.CompareRowVersion;

// データベースに変更を書き戻し

da.Update(datTable);

}

static private string GetConnectionString( )

{

' 設定ファイルより接続文字列の取得

return "Data Source=localhost;Initial Catalog=AdventureWorks;"

+ "Integrated Security=SSPI";

}

}

■ DataSet への既存の制約の追加

DataAdapter の Fill メソッドは、DataSet にデータソースからのテーブルの列と行丈を格納する。制

約は一般にデータソースで設定されるが、既定では Fill メソッドは DataSet にスキーマ情報を追加し

ない。データソースからの既存の主キー制約情報を DataSet に設定するには、DataAdapter の

FillSchema メソッドを呼び出すか、又は、Fill を呼び出す前に DataAdapter の MissingSchemaAction

プロパティを AddWithKey に設定する。此れに依り、データソースの主キー制約が DataSet の主キー

制約に反映される。外部キー制約情報はインクルードされない為、「テーブルへの制約の追加」で示す

様に明示的に作成する必要が有る。

Page 33: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-33-

DataSet 内にデータを格納する前にスキーマ情報を追加すると、主キー制約が DataSet 内の DataTable

オブジェクトにインクルードされる。其の結果、DataSet に対して格納を行う追加の呼出を行った時、

其の主キー列情報を使用してデータソースから得られた新しい行と各 DataTable の現在の行を一致さ

せ、各テーブルの現在のデータをデータソースのデータで上書きする。スキーマ情報が無いと、DataSet

にデータソースからの新しい行が付け加えられ、重複行が発生する。

※ データソースの列を自動インクリメント列と仕て指定した場合は、FillSchema メソッド

(MissingSchemaAction を AddWithKey に設定した Fill メソッド)が、AutoIncrement プロパテ

ィを trueに設定したDataColumn を作成する。但し、AutoIncrementStep値と AutoIncrementSeed

値は明示的に設定する必要が有る。自動インクリメント列の詳細に付いては、「AutoIncrement 列の

作成」を参照され度い。

FillSchema の使用や MissingSchemaAction の設定を AddWithKey にする場合、データソースで主キ

ー列情報を確認する為の追加の処理が必要に成る。此の追加の処理に依りパフォーマンスが低下する場

合が有る。デザイン時に主キー情報が解って居る場合は、最適のパフォーマンスを得る為に主キー列(複

数の場合も有る)を明示的に指定する事を推奨する。テーブルに関する主キー情報を明示的に設定する

方法に付いては、「テーブルの主キーの定義」を参照され度い。

FillSchema を使用して DataSet にスキーマ情報を追加する方法を次のコード例に示す。

Visual Basic

Dim custDataSet As DataSet = New DataSet( ) custAdapter.FillSchema(custDataSet, SchemaType.Source, "Customers")

custAdapter.Fill(custDataSet, "Customers")

C#

DataSet custDataSet = new DataSet( ); custAdapter.FillSchema(custDataSet, SchemaType.Source, "Customers");

custAdapter.Fill(custDataSet, "Customers");

Fill メソッドのMissingSchemaAction.AddWithKey プロパティを使用してスキーマ情報を DataSetに

追加する方法を次のコード例に示す。

Visual Basic

Dim custDataSet As DataSet = New DataSet( )

custAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey

custAdapter.Fill(custDataSet, "Customers")

C#

DataSet custDataSet = new DataSet( ); custAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey;

custAdapter.Fill(custDataSet, "Customers");

複数結果セット

DataAdapter は、SelectCommand から返された複数の結果セットを検出すると DataSet に複数のテー

ブルを作成する。此等のテーブルには、0 から始まるインクリメンタル既定名 TableN が割り当てられ

る。従って Table0 ではなく、Table から始まる。テーブル名が FillSchema メソッドに引数と仕て渡さ

れると、0 からから始まるインクリメンタル名 TableNameN が割り当てられる。此処では、TableName0

ではなく TableName から始まる。

Page 34: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-34-

※ OleDbDataAdapter オブジェクトの FillSchema メソッドが複数の結果セットを返すコマンドと仕

て呼び出された場合は、最初の結果セットのスキーマ情報が返される。OleDbDataAdapter を使用

して複数の結果セットのスキーマ情報を返す時は、Fill メソッドを呼び出す時に AddWithKey に設

定した MissingSchemaAction を指定してスキーマ情報を取得する事を推奨する。

■ DataAdapter イベントの使用

ADO.NET DataAdapter は、データソースのデータに対して行われた変更に応答する為に使用出来る 3

つのイベントを公開する。DataAdapter イベントを次の表に示す。

イベント 説明

RowUpdating 行に対する UPDATE、INSERT、DELETE の各操作が(Update メソッドの 1 つの呼出に依って)

開始し様と仕て居る。

RowUpdated 行に対する UPDATE、INSERT、DELETE の各操作が(Update メソッドの 1 つの呼出に依って)

完了した。

FillError Fill 操作中にエラーが発生した。

RowUpdating と RowUpdated

RowUpdating は、DataSet 側で生じた行に対する更新が、データソース側で処理される前に発生する。

RowUpdated は、DataSet 側で生じた行に対する更新が、データソース側で処理された後で発生する。

従って、更新が始まる前に RowUpdating を使用して更新の動作を変更する事で、更新発生時に行う追

加の処理の提供、更新行への参照の保存、現在の更新のキャンセル、後で処理するバッチ処理の為の更

新スケジュール等を提供出来る。RowUpdated は、更新中に発生するエラーや例外の応答に便利で有る。

DataSet にエラー情報や再試行ロジック等を追加出来る。

RowUpdating イベントや RowUpdated イベントに渡される RowUpdatingEventArgs 引数と

RowUpdatedEventArgs 引数には、更新を実行する為に使用される Command オブジェクトを参照す

る Command プロパティ、更新情報を格納する DataRow オブジェクトを参照する Row プロパティ、

何のタイプの更新を実行するかを示す StatementType プロパティ、適用可能な場合は TableMapping、

及び、操作の Status 等が有る。

Status プロパティを使用すると、操作中にエラーが発生したか何うかを確認したり、必要に応じて現在

の行や結果行に対するアクションを制御したり出来る。イベントが発生すると、Status プロパティは

Continue 又は ErrorsOccurred の孰れかに成る。次の表では、更新の後続のアクションを制御する為に

Status プロパティに設定出来る値を示して居る。

ステータス 説明

Continue 更新操作を続行する。

ErrorsOccurred 更新操作を中止し、例外をスローする。

SkipCurrentRow 現在の行を無視し、更新操作を続行する。

SkipAllRemainingRows 更新操作を中止するが、例外はスローしない。

Status プロパティを ErrorsOccurred に設定すると、例外がスローされる。Errors プロパティを例外と

仕て設定する事で、何の例外をスローするかを制御出来る。Status に他の値を使用すると、例外はスロ

ーされない。

ContinueUpdateOnError プロパティを使用して更新行に関するエラーを処理する事も出来る。

DataAdapter.ContinueUpdateOnError を true に設定すると、行を更新した結果、例外がスローされ

様と仕て居る時に、例外のテキストを其の行の RowError 情報の中に格納し、例外をスローせずに処理

を続行出来る。此れに依り、Update が完了した時点でエラーに応答出来る様に成る。此れに対して

RowUpdated イベントを使用すると、エラーが発生した時点でエラーに応答出来る。

Page 35: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-35-

イベントハンドラを追加や削除する方法を次のコード例に示す。RowUpdating イベントハンドラは、

削除された総てのレコードのログをタイムスタンプと共に記録する。RowUpdated イベントハンドラは、

DataSet の行の RowError プロパティにエラー情報を追加し、例外をスローせずに処理を続行する

(ContinueUpdateOnError = true の動作を反映する)。

Visual Basic

' 変数 connection は、有効な SqlConnection オブジェクトとする。

Dim custAdapter As SqlDataAdapter = New SqlDataAdapter( _

"SELECT CustomerID, CompanyName FROM Customers", connection)

' ハンドルの追加

AddHandler custAdapter.RowUpdating, New SqlRowUpdatingEventHandler( _

AddressOf OnRowUpdating)

AddHandler custAdapter.RowUpdated, New SqlRowUpdatedEventHandler(

AddressOf OnRowUpdated)

' DataAdapter コマンドを設定し、DataSet を Fill し、DataSet を変更する。

custAdapter.Update(custDS, "Customers")

' ハンドルの削除

RemoveHandler custAdapter.RowUpdating, _

New SqlRowUpdatingEventHandler(AddressOf OnRowUpdating)

RemoveHandler custAdapter.RowUpdated, _

New SqlRowUpdatedEventHandler(AddressOf OnRowUpdated)

Private Shared Sub OnRowUpdating(sender As Object, args As SqlRowUpdatingEventArgs)

If args.StatementType = StatementType.Delete Then

Dim tw As System.IO.TextWriter = System.IO.File.AppendText("Deletes.log")

tw.WriteLine( _

"{0}: Customer {1} Deleted.", DateTime.Now, args.Row(_

"CustomerID", DataRowVersion.Original))

tw.Close( )

End If

End Sub

Private Shared Sub OnRowUpdated( sender As Object, args As SqlRowUpdatedEventArgs)

If args.Status = UpdateStatus.ErrorsOccurred

args.Status = UpdateStatus.SkipCurrentRow

args.Row.RowError = args.Errors.Message

End If

End Sub

C#

// 変数 connection は、有効な SqlConnection オブジェクトとする。

SqlDataAdapter custAdapter = new SqlDataAdapter(

"SELECT CustomerID, CompanyName FROM Customers", connection);

// ハンドルの追加

custAdapter.RowUpdating += new SqlRowUpdatingEventHandler(OnRowUpdating);

custAdapter.RowUpdated += new SqlRowUpdatedEventHandler(OnRowUpdated);

// DataAdapter コマンドを設定し、DataSet を Fill し、DataSet を変更する。

Page 36: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-36-

custAdapter.Update(custDS, "Customers");

// ハンドルの削除

custAdapter.RowUpdating -= new SqlRowUpdatingEventHandler(OnRowUpdating);

custAdapter.RowUpdated -= new SqlRowUpdatedEventHandler(OnRowUpdated);

protected static void OnRowUpdating(object sender, SqlRowUpdatingEventArgs args)

{

if (args.StatementType == StatementType.Delete)

{

System.IO.TextWriter tw = System.IO.File.AppendText("Deletes.log");

tw.WriteLine(

"{0}: Customer {1} Deleted.", DateTime.Now,

args.Row["CustomerID", DataRowVersion.Original]);

tw.Close( );

}

}

protected static void OnRowUpdated(object sender, SqlRowUpdatedEventArgs args)

{

if (args.Status == UpdateStatus.ErrorsOccurred)

{

args.Row.RowError = args.Errors.Message;

args.Status = UpdateStatus.SkipCurrentRow;

}

}

FillError

DataAdapter は、Fill 操作中にエラーが発生すると FillError イベントを発行する。此のエラーは、通

常、追加する行のデータを.NET Framework 型に変換した時に、有効桁を消失した場合に発生する。

Fill 操作中にエラーが発生した場合、現在の行は DataTable に追加されない。FillError イベントを使

用すると、エラーを解決して其の行を追加するか、又は、除外された行を無視し Fill 操作を続行出来る。

FillError イベントに渡す FillErrorEventArgs には、エラーに応答してエラーを解決出来る幾つかのプ

ロパティを含める事が出来る。FillErrorEventArgs オブジェクトのプロパティを次の表に示す。

プロパティ 説明

Errors 発生した Exception

DataTable エラー発生時にデータを格納し様と仕て居た DataTable オブジェクト

Values エラー発生時に追加しようと仕て居た行の値を保持して居るオブジェクトの配列

(Values 配列の序数参照は、追加しようと仕て居た行の列の序数参照に対応する。例え

ば、Values[0] は、行の第 1 列と仕て追加しようと仕た値で有る。)

Continue 例外をスローするか何うかを選択出来る。Continue プロパティを false に設定すると、

エラーが発生した時に現在の Fill 操作を停止し、例外をスローする。Continue を true

に設定すると、エラーに関係無く Fill 操作を続行する。

次に示すのは、DataAdapter の FillError イベントのイベントハンドラを追加するコード例で有る。此

の FillError イベント コードの例は、有効桁の消失が発生したか何うかを確認し、例外に応答する機会

を与える。

Page 37: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-37-

Visual Basic

AddHandler adapter.FillError, New FillErrorEventHandler( AddressOf FillError)

Dim dataSet As DataSet = New DataSet

adapter.Fill(dataSet, "ThisTable")

Private Shared Sub FillError(sender As Object, args As FillErrorEventArgs)

' 精度が失われた時の処理

If args.Errors.GetType( ) Is Type.GetType("System.OverflowException") Then

' 最初の 2 列を用いてテーブルに行の追加

DataRow myRow = args.DataTable.Rows.Add(New Object( ) _

{args.Values(0), args.Values(1), DBNull.Value})

' 第 3 列の値を含む RowError の設定

args.RowError = _

"OverflowException encountered. Value from data source: " & args.Values(2)

args.Continue = True

End If

End Sub

C#

adapter.FillError += new FillErrorEventHandler(FillError);

DataSet dataSet = new DataSet( );

adapter.Fill(dataSet, "ThisTable"); protected static void FillError(object sender, FillErrorEventArgs args)

{

// 精度が失われた時の処理

if (args.Errors.GetType( ) == typeof(System.OverflowException))

{

// 最初の 2 列を用いてテーブルに行の追加

DataRow myRow = args.DataTable.Rows.Add(new object[]

{args.Values[0], args.Values[1], DBNull.Value});

// 第 3 列の値を含む RowError の設定

args.RowError =

"OverflowException Encountered. Value from data source: " + args.Values[2];

args.Continue = true;

}

}

■ ID 値と Autonumber 値の取得

DataTable の列を自動的にインクリメントする主キーと仕て設定する事に依り、テーブルの各行に一意

の値が設定される様にする。併し、アプリケーションに対して複数のクライアントが存在し、各クライ

アントが夫々れの DataTable インスタンスを使用して居る場合も有る。其の場合は、DataTable の個別

のインスタンスに最終的に同じデータが格納される様にする必要が有る。総てのクライアントが同じ 1

つのデータソースを使用する為、データソースに自動インクリメント値を定義する事で、此の様な競合

を解決出来る。自動インクリメント値を定義するには、Microsoft SQL Server の ID 列か Microsoft

Access の Autonumber フィールドを使用する。

データソースを使用してDataSetに追加された新しい行の ID列やAutonumber列にデータを読み込む

と、DataSet が直接データソースに接続されて居ない為に特有の状況が発生する。其の結果、DataSet

は、データソースに依って自動的に生成された値を認識出来なく成る。併し、出力パラメータを持つス

トアドプロシージャを作成出来る Microsoft SQL Server 等のデータソースでは、新しい ID 値等の自動

Page 38: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-38-

生成値を出力パラメータと仕て指定し、DataAdapter を使用して其の値を DataSet 内の対応する列に

割り当てる事が出来る。

データソースが、出力パラメータを持つストアドプロシージャをサポートして居ない場合も有る。其の

場合は RowUpdated イベントを使用して自動生成値を取得し、其の値を DataSet 内の挿入、又は、更

新された列に配置出来る。此のセクションでは、Microsoft Access 2000 以降で Jet 4.0 OLE DB プロバ

イダを使用して RowUpdated イベントにコードを追加する事に依り、挿入が発生したか何うかを確認

し、自動インクリメント値を取得し、其の値を現在の更新行に格納する方法を示すサンプルを示す。

SQL Server の ID 列値の取得

次のストアドプロシージャ、及び、コード例では、Microsoft SQL Server テーブルの自動的にインクリ

メントされた ID 値を、DataSet のテーブルに追加された行の対応する列に割り当てる方法を示す。此

のストアドプロシージャは、Northwind データベースの Categories テーブルに新しい行を挿入し、

Transact-SQL SCOPE_IDENTITY 関数から返された ID 値を出力パラメータと仕て返す為に使用され

て居る。

CREATE PROCEDURE InsertCategory

@CategoryName nchar(15),

@Identity int OUT

AS

INSERT INTO Categories (CategoryName) VALUES(@CategoryName)

SET @Identity = SCOPE_IDENTITY( )

其の後、InsertCategory ストアドプロシージャを InsertCommand のソースと仕て指定出来る。ID 出

力パラメータを受け取るパラメータが作成される。作成されたパラメータは、 Output の

ParameterDirection と、DataSet のローカル Categories テーブルの CategoryID 列と仕て指定された

SourceColumn を持って居る。追加された行に対して InsertCommand が処理されると、自動インクリ

メント ID 値が出力パラメータと仕て返され、現在の行の CategoryID 列に設定される。

自動インクリメント値を出力パラメータと仕て返し、其の値を DataSet の CategoryID 列のソース値と

仕て指定する方法を次のコード例に示す。

Visual Basic

' 変数 connection は、有効な SqlConnection オブジェクトとする。

Dim adapter As SqlDataAdapter = New SqlDataAdapter( _

"SELECT CategoryID, CategoryName FROM dbo.Categories", connection)

adapter.InsertCommand = New SqlCommand("InsertCategory", connection)

adapter.InsertCommand.CommandType = CommandType.StoredProcedure

adapter.InsertCommand.Parameters.Add( _

"@CategoryName", SqlDbType.NChar, 15, "CategoryName")

Dim parameter As SqlParameter = adapter.InsertCommand.Parameters.Add( _

"@Identity", SqlDbType.Int, 0, "CategoryID")

parameter.Direction = ParameterDirection.Output

connection.Open( )

Dim categories As DataSet = New DataSet

adapter.Fill(categories, "Categories")

Page 39: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-39-

Dim newRow As DataRow = categories.Tables("Categories").NewRow( )

newRow("CategoryName") = "New Category"

categories.Tables("Categories").Rows.Add(newRow)

adapter.Update(categories, "Categories")

connection.Close( )

C#

// 変数 connection は、有効な SqlConnection オブジェクトとする。

SqlDataAdapter adapter = new SqlDataAdapter(

"SELECT CategoryID, CategoryName FROM dbo.Categories", connection);

adapter.InsertCommand = new SqlCommand("InsertCategory", connection);

adapter.InsertCommand.CommandType = CommandType.StoredProcedure;

adapter.InsertCommand.Parameters.Add(

"@CategoryName", SqlDbType.NChar, 15, "CategoryName");

SqlParameter parameter = adapter.InsertCommand.Parameters.Add(

"@Identity", SqlDbType.Int, 0, "CategoryID");

parameter.Direction = ParameterDirection.Output;

connection.Open( );

DataSet categories = new DataSet( );

adapter.Fill(categories, "Categories");

DataRow newRow = categories.Tables["Categories"].NewRow( );

newRow["CategoryName"] = "New Category";

categories.Tables["Categories"].Rows.Add(newRow);

adapter.Update(categories, "Categories");

connection.Close( );

Microsoft Access の Autonumber 値の取得

Microsoft Access では、ストアドプロシージャやバッチコマンド処理がサポートされて居ない為、上の

例の様にテーブルのソース列に出力パラメータを割り当てる事は出来ない。併し、Microsoft Access

2000 以降では、INSERT 後に Autonumber フィールドの値を取得する、@@IDENTITY プロパティが

サポートされて居る。RowUpdated イベントを使用すると、INSERT が発生したか何うかを確認し、

最新の Autonumber 値を取得し、其の値を DataSet のローカル テーブルの ID 列に配置出来る。

OleDbDataAdapter を使用して、Microsoft Access 2000 Northwind データベースの Categories テーブ

ルに新しい値を挿入する方法を次のコード例に示す。此の例では、レコードが Categories テーブルに

挿入された時に、Jet エンジンに依って生成された Autonumber 値を、RowUpdated イベント、及び

Access データベースを使用して格納して居る。此の操作を実行出来るのは、Jet 4.0 OLE DB プロバイ

ダ、及び、Microsoft Access 2000 以降丈で有る。

Visual Basic

' 変数 connection は、有効な OleDbConnection オブジェクトとする。

Dim adapter As OleDbDataAdapter = New OleDbDataAdapter( _

"SELECT CategoryID, CategoryName FROM Categories ORDER BY CategoryID", connection)

Page 40: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-40-

adapter.InsertCommand = New OleDbCommand( _

"INSERT INTO Categories (CategoryName) Values(?)", connection)

adapter.InsertCommand.CommandType = CommandType.Text

adapter.InsertCommand.Parameters.Add( _

"@CategoryName", OleDbType.Char, 15, "CategoryName")

connection.Open( )

' DataSet の Fill

Dim categories As DataSet = New DataSet

adapter.Fill(categories, "Categories")

' 新しい行の追加

Dim newRow As DataRow = categories.Tables("Categories").NewRow( )

newRow("CategoryName") = "New Category"

categories.Tables("Categories").Rows.Add(newRow)

' ハンドルの追加

AddHandler adapter.RowUpdated, _

New OleDbRowUpdatedEventHandler(AddressOf OnRowUpdated)

' DataSet の更新

adapter.Update(categories, "Categories")

connection.Close( )

' OnRowUpdated のイベントプロシージャ

Private Shared Sub OnRowUpdated( sender As Object, args As OleDbRowUpdatedEventArgs)

Dim newID As Integer = 0

Dim idCMD As OleDbCommand = New OleDbCommand( _

"SELECT @@IDENTITY", connection)

If args.StatementType = StatementType.Insert

newID = CInt(idCMD.ExecuteScalar( ))

args.Row("CategoryID") = newID

End If

End Sub

C#

// 変数 connection は、有効な OleDbConnection オブジェクトとする。

OleDbDataAdapter adapter = new OleDbDataAdapter(

"SELECT CategoryID, CategoryName FROM Categories ORDER BY CategoryID",

connection);

adapter.InsertCommand = new OleDbCommand(

"INSERT INTO Categories (CategoryName) Values(?)", connection);

adapter.InsertCommand.CommandType = CommandType.Text;

adapter.InsertCommand.Parameters.Add( _

"@CategoryName", OleDbType.Char, 15, "CategoryName");

connection.Open( );

// DataSet の Fill

DataSet categories = new DataSet( );

Page 41: データベースjunko036.html.xdomain.jp/pdf/database/DataAdapter2.pdfSQL Server データベースに接続する場合は、関連付けられた SqlCommand オブジェクトや

VS 2005 資料 【電脳梁山泊 烏賊塾】

-41-

adapter.Fill(categories, "Categories");

// 新しい行の追加

DataRow newRow = categories.Tables["Categories"].NewRow( );

newRow["CategoryName"] = "New Category";

categories.Tables["Categories"].Rows.Add(newRow);

// ハンドルの追加

adapter.RowUpdated += new OleDbRowUpdatedEventHandler(OnRowUpdated);

// DataSet の更新

adapter.Update(categories, "Categories");

connection.Close( );

// OnRowUpdated のイベントプロシージャ

protected static void OnRowUpdated(object sender, OleDbRowUpdatedEventArgs args)

{

int newID = 0;

OleDbCommand idCMD = new OleDbCommand(

"SELECT @@IDENTITY", connection);

if (args.StatementType == StatementType.Insert)

{

// Retrieve the identity value and store it in the CategoryID column.

newID = (int)idCMD.ExecuteScalar( );

args.Row["CategoryID"] = newID;

}

}