自動生成コマンド
SelectCommand が実行時に動的に指定される場合、たとえばクエリ ツールを使用してユーザーの記述したクエリ構文を解釈する場合は、デザイン時に適切な InsertCommand、UpdateCommand、または DeleteCommand を指定することはできません。DataTable を単一データベース テーブルに割り当てたり、単一データベースから生成する場合は、CommandBuilder オブジェクトを利用して自動的に DataAdapter の DeleteCommand、InsertCommand、および UpdateCommand を生成できます。
自動的なコマンド生成のための最低限の条件として、SelectCommand プロパティを設定する必要があります。SelectCommand で取得したテーブル スキーマによって、自動的に生成される INSERT、UPDATE、DELETE の各ステートメントの構文が決まります。
CommandBuilder は、INSERT、UPDATE、DELETE の各コマンドを生成するために SelectCommand を実行する必要があります。その結果、データ ソースへの追加のトリップが必要になるため、パフォーマンスの低下する可能性があります。最適のパフォーマンスを得るには、明示的にコマンドを指定し、CommandBuilder を使用しないようにします。
SelectCommand は少なくとも 1 つの主キーまたは一意の列を返す必要があります。どちらも存在しない場合は、InvalidOperation 例外が生成され、コマンドは生成されません。
DataAdapter との関連付けが行われていて、DataAdapter の InsertCommand、UpdateCommand、DeleteCommand の各プロパティが null 参照である場合、CommandBuilder は自動的にこれらのプロパティを生成します。プロパティに対して既に Command が存在する場合は、既存の Command が使用されます。
複数のテーブルを結合して作成したデータベース ビューは、単一データベース テーブルとは見なされません。たとえば、コマンドは CommandBuilder を使用すると自動的に生成できないため、明示的に指定する必要がでてきます。DataSet に対する更新を元のデータ ソースに反映させるコマンドを明示的に設定する方法については、「DataAdapter および DataSet によるデータベースの更新」を参照してください。
出力パラメータを DataSet の更新行に割り当てることが必要な場合があります。一般的なタスクの 1 つは、データ ソースの自動的に生成された ID フィールドまたはタイムスタンプの値を取得することです。CommandBuilder は、既定では更新行の列に出力パラメータを割り当てません。その場合は、コマンドを明示的に指定する必要があります。自動的に生成された ID フィールドを挿入行の列に割り当てる例については、「ID 値および Autonumber 値の取得」を参照してください。
コマンド自動生成規則
コマンド自動生成の規則を次の表に示します。
コマンド | 規則 |
---|---|
InsertCommand | RowState の設定が DataRowState.Added であるテーブル内のすべての行に対して、データ ソースの行を挿入する。更新可能なすべての列に対して値を挿入します。ただし、ID、式、タイムスタンプなどの列は除きます。 |
UpdateCommand | RowState の設定 DataRowState.Modified に設定されているテーブル内のすべての行に対して、データ ソース側での行を更新する。ID や式などの更新不可能な列を除く、すべての列の値を更新します。データ ソースの列の値と該当行の主キー列の値が一致し、さらにデータ ソースのその他の列がその行の元の値と一致する、すべての行を更新します。詳細については、「更新および削除のオプティミスティック同時実行制御」の該当するセクションを参照してください。 |
DeleteCommand | RowState が DataRowState.Deleted に設定されているテーブル内のすべての行についてデータ ソース側で行を削除する。列の値とその行の主キー列の値が一致し、さらにデータ ソースのその他の列が、その行の元の値と一致する、すべての行を削除します。詳細については、「更新および削除のオプティミスティック同時実行制御」の該当するセクションを参照してください。 |
更新および削除のオプティミスティック同時実行制御
UPDATE ステートメントおよび DELETE ステートメントに対する自動コマンド生成ロジックは、オプティミスティック同時実行制御に基づいています。これはつまり、編集時にレコードがロックされず、他のユーザーまたはプロセスがそのレコードをいつでも変更できることを意味します。レコードは SELECT コマンドによって返された後、UPDATE ステートメントまたは DELETE ステートメントの実行前に変更されている可能性もあるため、自動的に生成される UPDATE ステートメントまたは DELETE ステートメントには、元のすべての値を含み、データ ソースから削除されていない行だけを更新するように指定した WHERE 句が含まれます。これにより、新しいデータが上書きされるのを防ぎます。自動的に生成された更新コマンドが削除済みまたは DataSet にある元の値が含まれていない行を更新しようとすると、コマンドはどのレコードにも反映されずに、DBConcurrencyException がスローされます。
元の値とは関係なく UPDATE または DELETE を実行する場合は、DataAdapter に明示的に UpdateCommand を設定し、自動コマンド生成は行わないでください。
自動コマンド生成ロジックの制限事項
自動コマンド生成には次の制限事項が適用されます。
リレーションシップのないテーブルに限定
自動コマンド生成ロジックでは、データ ソースの他のテーブルへのリレーションシップを考慮せずに、独立したテーブルを対象として INSERT、UPDATE、または DELETE の各ステートメントを生成します。その結果、Update を呼び出してデータベースの外部キー制約に関係する列に対する変更を発行すると、エラーが発生する場合があります。このような例外を防ぐには、外部キー制約に関係する列の更新には CommandBuilder を使用せず、更新操作を実行するステートメントを明示的に指定します。
テーブル名と列名
列名またはテーブル名にスペース、ピリオド (.)、疑問符 (?)、引用符、その他の英数字以外の特殊文字が含まれていると、たとえそれらの文字が角かっこで囲まれていても、自動コマンド生成ロジックはエラーになります。catalog.schema.table
の形式をとる完全修飾テーブル名はサポートされています。
CommandBuilder による SQL ステートメントの自動生成
DataAdapter に対して SQL ステートメントを自動的に生成するには、まず DataAdapter の SelectCommand プロパティを設定します。次に、CommandBuilder オブジェクトを作成し、CommandBuilder で SQL ステートメントを自動的に生成する DataAdapter を引数として指定します。
Dim custDA As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers", nwindConn)
Dim custCB As SqlCommandBuilder = New SqlCommandBuilder(custDA)
custCB.QuotePrefix = "["
custCB.QuoteSuffix = "]"
Dim custDS As DataSet = New DataSet
nwindConn.Open()
custDA.Fill(custDS, "Customers")
' Code to modify data in the DataSet here.
' Without the SqlCommandBuilder, this line would fail.
custDA.Update(custDS, "Customers")
nwindConn.Close()
[C#]
SqlDataAdapter custDA = new SqlDataAdapter("SELECT * FROM Customers", nwindConn);
SqlCommandBuilder custCB = new SqlCommandBuilder(custDA);
custCB.QuotePrefix = "[";
custCB.QuoteSuffix = "]";
DataSet custDS = new DataSet();
nwindConn.Open();
custDA.Fill(custDS, "Customers");
// Code to modify data in the DataSet here.
// Without the SqlCommandBuilder, this line would fail.
custDA.Update(custDS, "Customers");
nwindConn.Close();
SelectCommand の変更
INSERT、UPDATE、または DELETE の各コマンドを自動生成した後に SelectCommand の CommandText を変更すると、例外が発生します。変更された SelectCommand.CommandText に、INSERT、UPDATE、または DELETE の各コマンドの自動生成時に使用した SelectCommand.CommandText と矛盾するスキーマ情報が含まれている場合、後続の DataAdapter.Update メソッド呼び出しでアクセスする列は SelectCommand によって参照された現在のテーブルには存在しない可能性があり、例外が発生します。
CommandBuilder の RefreshSchema メソッドを呼び出すことで、自動的にコマンドを生成する CommandBuilder が使用するスキーマ情報を更新できます。
どのコマンドが自動的に生成されたかを確認するには、CommandBuilder オブジェクトの GetInsertCommand、GetUpdateCommand、GetDeleteCommand の各メソッドを使用して、自動的に生成されたコマンドへの参照を取得し、関連付けられている Command の CommandText プロパティをチェックします。
自動的に生成された更新コマンドをコンソールに出力するサンプル コードを次に示します。
Console.WriteLine(custCB.GetUpdateCommand().CommandText)
次に示すのは、前述の例 (「CommandBuilder による SQL ステートメントの自動生成」にあるセクションの例) の続きのコードです。この例では、CompanyName 列を ContactName 列に置き換えて Customers テーブルを再作成します。RefreshSchema メソッドを呼び出し、新しい列情報を使用して、自動的に生成されたコマンドを更新します。
nwindConn.Open()
custDA.SelectCommand.CommandText = "SELECT CustomerID, ContactName FROM Customers"
custCB.RefreshSchema()
custDS.Tables.Remove(custDS.Tables("Customers"))
custDA.Fill(custDS, "Customers")
' Code to modify the new table in the DataSet here.
' Without the call to RefreshSchema, this line would fail.
custDA.Update(custDS, "Customers")
nwindConn.Close()
[C#]
nwindConn.Open();
custDA.SelectCommand.CommandText = "SELECT CustomerID, ContactName FROM Customers";
custCB.RefreshSchema();
custDS.Tables.Remove(custDS.Tables["Customers"]);
custDA.Fill(custDS, "Customers");
// Code to modify the new table in the DataSet here.
// Without the call to RefreshSchema, this line would fail.
custDA.Update(custDS, "Customers");
nwindConn.Close();
参照
.NET Framework データ プロバイダによるデータのアクセス | OleDbDataAdapter クラス | OdbcDataAdapter クラス | SqlDataAdapter クラス