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