共用方式為


自動產生的命令

如果 SelectCommand 是在 Runtime 時以動態方式指定 (例如透過能接收使用者下達文字命令的查詢工具),則您可能無法於設計階段指定適當的 InsertCommandUpdateCommandDeleteCommand。如果您的 DataTable 對應至或產生自單一資料庫資料表,則您可以利用 CommandBuilder 物件自動產生 DataAdapterDeleteCommandInsertCommandUpdateCommand

根據最小需求,您必須設定 SelectCommand 屬性,才能使用自動命令產生功能。SelectCommand 擷取的資料表結構描述則決定自動產生的 INSERT、UPDATE 和 DELETE 陳述式語法。

CommandBuilder 必須執行 SelectCommand,才能傳回必要的中繼資料 (Metadata) 來建構插入、更新和刪除命令,因此必須要另外連至資料來源,但是這會降低效能。若要達到最佳效能,請您明確地指定您的命令,而不要使用 CommandBuilder

SelectCommand 還必須傳回至少一個主索引鍵或唯一的資料行。如果兩個都不存在,便會發生 InvalidOperation 例外狀況,且不會產生命令。

CommandBuilderDataAdapter 關聯時,會自動產生 DataAdapterInsertCommandUpdateCommandDeleteCommand 屬性 (如果它們是 Null 參考)。如果屬性的 Command 已經存在,則會使用現有的 Command

您不能將兩個或多個資料表合併所建立的資料庫檢視視為單一資料庫資料表。這種情況下,您不能使用 CommandBuilder 自動產生命令,而需要明確地指定您的命令。如需明確設定命令以將 DataSet 的更新解析回資料來源的詳細資訊,請參閱使用 DataAdapter 和 DataSet 更新資料庫

您可能想要把輸出參數對應回 DataSet 已更新的資料列。一項通用工作便是從資料來源擷取自動產生的識別欄位值或時間戳記值。依預設,CommandBuilder 並不會將輸出參數對應至更新資料列內的資料行。這種情形下,您必須明確地指定命令。如需範例瞭解自動產生識別欄位如何對應至插入資料列資料行,請參閱擷取識別或 Autonumber 值

自動產生命令的規則

下列表格顯示產生自動產生命令的規則。

命令 規則
InsertCommand 到資料來源中,找出資料表內 RowStateDataRowState.Added 的所有資料列,將資料列插入。將數值插入所有可更新的資料行 (但識別、運算式或時間戳記的資料行除外)。
UpdateCommand 到資料來源中,找出資料表內 RowStateDataRowState.Modified 的所有資料列,將資料列更新。更新所有資料行的值,除了不可更新的資料行以外,例如識別或運算式。並且在資料來源中,找出資料行值與資料列主索引鍵資料行值相符的資料列,以及剩餘資料行與資料列原始資料相符的資料列,將這些資料列全都更新。如需詳細資訊,請參閱這個主題中的<更新和刪除的樂觀並行模式>章節。
DeleteCommand 到資料來源中,找出資料表內 RowStateDataRowState.Deleted 的所有資料列,將資料列刪除。並且在資料來源中,找出資料行值與資料列主索引鍵資料行值相符的資料列,以及剩餘資料行與資料列原始資料相符的資料列,將這些資料列全都刪除。如需詳細資訊,請參閱這個主題中的<更新和刪除的樂觀並行模式>章節。

更新和刪除樂觀並行模式

自動替 UPDATE 和 DELETE 陳述式產生命令的邏輯是以樂觀並行為基礎;也就是說,資料錄不會被鎖定進行編輯,也可隨時由其他使用者或處理序修改。資料錄從 SELECT 陳述式傳回後可能已經修改,但是發出 UPDATE 或 DELETE 陳述式之前,自動產生的 UPDATE 或 DELETE 陳述式會包含 WHERE 子句,所以,只有資料列包含所有原始值且尚未從資料來源刪除時,資料列才會被更新。這樣是為了避免新資料被覆寫。為了避免自動產生的更新命令嘗試將 DataSet 中已經刪除或不包含原始值的資料列更新,這類命令不會影響任何資料列,且會導致 DBConcurrencyException 擲回。

如果您想完成 UPDATE 或 DELETE (不管是否為原始資料),則您需要明確地設定 DataAdapterUpdateCommand,而不是依賴自動命令產生功能。

自動命令產生的邏輯限制

下列限制適用於自動命令產生。

僅限不相關的資料表

自動命令產生邏輯為獨立資料表產生 INSERT、UPDATE 或 DELETE 陳述式,而不顧及任何與資料來源中其他資料表的關聯。所以,如果呼叫 Update 將變更送出至資料行,而這個資料行又擔任資料庫的外部索引鍵條件約束,則可能會發生錯誤。為了避免發生這種例外狀況,請勿使用 CommandBuilder 來更新擔任外部索引鍵條件約束的資料行,反而應該明確地指定執行作業的陳述式。

資料表和資料行名稱

如果資料行名稱或資料表名稱包含任何特殊字元,例如空格、句號、引號或其他非英數的字元,則即使以括號分隔,自動命令產生邏輯還是會失敗。格式為 catalog.schema.table 的完整資料表名稱則有支援。

使用 CommandBuilder 自動產生 SQL 陳述式

若要為 DataAdapter 自動產生 SQL 陳述式,首先請設定 DataAdapterSelectCommand 屬性,接著建立 CommandBuilder 物件並將 DataAdapter 指定為引數,供 CommandBuilder 自動產生 SQL 陳述式。

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

如果您在插入、更新或刪除命令自動產生後,修改 SelectCommandCommandText,便可能發生例外狀況。如果已修改的 SelectCommand.CommandText 包含的結構描述資訊與插入、更新或刪除命令自動產生時使用的 SelectCommand.CommandText 不一致,日後呼叫的 DataAdapter.Update 方法便可能會到 SelectCommand 參考的目前資料表中,嘗試存取已不存在的資料行,這樣就會擲回例外狀況。

您可以藉由重新整理 CommandBuilder 使用的結構描述資訊,呼叫 CommandBuilderRefreshSchema 方法以自動產生命令。

若您想知道自動產生的命令為何,您可以使用 CommandBuilder 物件的 GetInsertCommandGetUpdateCommandGetDeleteCommand 方法,取得至自動產生命令的參考,然後檢查相關 CommandCommandText 屬性。

下列程式碼範例將自動產生的命令寫入主控台。

Console.WriteLine(custCB.GetUpdateCommand().CommandText)

下列範例繼續前面範例的程式碼 (在<使用 CommandBuilder 自動產生 SQL 陳述式>章節中),並重新建立 Customers 資料表,以 ContactName 資料行取代 CompanyName 資料行。呼叫 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 類別