共用方式為


將資料儲存回 .NET Framework 應用程式中的資料庫

備註

類別 DataSet 和相關類別是 2000 年代初的舊版 .NET Framework 技術,可讓應用程式在應用程式與資料庫中斷連線時使用記憶體中的數據。 這些技術特別適用於可讓使用者修改數據並將變更保存回資料庫的應用程式。 雖然數據集是經過證實的成功技術,但新 .NET 應用程式的建議方法是使用 Entity Framework Core。 Entity Framework 提供更自然的方式,以表格式數據作為物件模型使用,而且具有更簡單的程序設計介面。

數據集是數據的記憶體內部復本。 如果您修改該數據,最好將這些變更儲存回資料庫。 您可以使用下列三種方式之一來執行此動作:

  • 呼叫 TableAdapter 的 Update 其中一個方法

  • 呼叫 TableAdapter 的某個 DBDirect 方法

  • 藉由在Visual Studio產生的 TableAdapterManager 上呼叫 UpdateAll 方法,當數據集包含與數據集中其他數據表相關的數據表時,

當您將資料集的資料表綁定至 Windows Form 或 XAML 頁面上的控制項時,資料繫結架構會為您完成所有工作。

如果您熟悉 TableAdapters,可以直接跳到下列其中一個主題:

主題 說明
將新記錄插入資料庫中 如何使用 TableAdapters 或 Command 物件執行更新和插入
使用 TableAdapter 更新數據 如何使用 TableAdapters 執行更新
階層式更新 如何使用兩個或多個相關數據表從數據集執行更新
處理並行例外狀況 當兩位用戶同時嘗試變更資料庫中相同的數據時,如何處理例外狀況
如何:使用交易儲存數據 如何使用系統在交易中儲存數據。 Transactions 命名空間和 TransactionScope 物件
將數據儲存在交易中 建立 Windows Forms 應用程式的慢步解說,示範將資料儲存到交易內的資料庫
將資料儲存至資料庫 (多個資料表) 如何編輯記錄,並將多個數據表中的變更儲存回資料庫
將數據從物件儲存到資料庫 如何使用 TableAdapter DbDirect 方法,將數據從不在數據集中的對象傳遞至資料庫
使用 TableAdapter DBDirect 方法儲存數據 如何使用 TableAdapter 將 SQL 查詢直接傳送至資料庫
將數據集儲存為 XML 如何將數據集儲存至 XML 檔

雙階段更新

更新資料來源是兩個步驟的過程。 第一個步驟是使用新的記錄、變更的記錄或刪除的記錄來更新數據集。 如果您的應用程式永遠不會將這些變更傳回數據源,則表示您已完成更新。

如果您將變更傳回資料庫,則需要第二個步驟。 如果您未使用數據綁定控件,則必須手動呼叫 Update 用來填入數據集之相同 TableAdapter (或數據配接器)的方法。 不過,您也可以使用不同的配接器,例如,將數據從某個數據源移至另一個數據源,或更新多個數據源。 如果您未使用數據系結,而且正在儲存相關數據表的變更,則必須手動具現化自動產生 TableAdapterManager 類別的變數,然後呼叫其 UpdateAll 方法。

數據集更新的概念圖

數據集包含數據表集合,其中包含數據列的集合。 如果您想要稍後更新基礎數據來源,您必須在新增或移除資料列時,在屬性上使用 DataTable.DataRowCollection 方法。 這些方法會執行更新數據源所需的變更追蹤。 如果您在 Rows 屬性上呼叫 RemoveAt 集合,則不會將刪除傳回資料庫。

合併數據集

您可以將數據集的內容與另一個數據集 合併 ,以更新數據集的內容。 這牽涉到將 數據集的內容複製到呼叫的數據集(稱為 目標 數據集)。 當您合併數據集時,來源數據集中的新記錄會新增至目標數據集。 此外,來源數據集中的其他數據列會新增至目標數據集。 當您有本機數據集,並從另一個應用程式取得第二個數據集時,合併數據集很有用。 當您從 XML Web 服務等元件取得第二個數據集,或需要從多個數據集整合數據時,它也很有用。

合併數據集時,您可以傳遞 Boolean 自變數 (preserveChanges),告知 Merge 方法是否要在目標數據集中保留現有的修改。 由於數據集會維護多個版本的記錄,因此務必注意合併多個版本的記錄。 下表顯示兩個資料集中的記錄如何合併:

DataRowVersion 目標數據集 來源資料集
原版 詹姆斯·威爾遜 詹姆斯·威爾遜
目前 吉姆·威爾遜 詹姆斯·威爾遜

在上一個數據表上呼叫Merge方法,使用preserveChanges=false targetDataset.Merge(sourceDataset)產生下列數據:

DataRowVersion 目標數據集 來源資料集
原版 詹姆斯·威爾遜 詹姆斯·威爾遜
目前 詹姆斯·威爾遜 詹姆斯·威爾遜

呼叫Merge方法使用preserveChanges = true targetDataset.Merge(sourceDataset, true)結果為下列資料:

DataRowVersion 目標數據集 來源資料集
原版 詹姆斯·威爾遜 詹姆斯·威爾遜
目前 吉姆·威爾遜 詹姆斯·威爾遜

謹慎

preserveChanges = true這個情境中,如果在目標數據集的記錄上呼叫RejectChanges方法,則會還原為數據集的原始數據。 這表示如果您嘗試使用目標數據集更新原始數據源,它可能無法找到要更新的原始數據列。 您可以將來自數據源中已更新的記錄填入另一個數據集,然後執行合併以防止並發違規。 (當另一位使用者在數據集填滿之後修改數據源中的記錄時,就會發生並行違規。

更新條件約束

若要變更現有的數據列,請在個別數據行中新增或更新數據。 如果數據集包含條件約束(例如外鍵或不可為 Null 的條件約束),記錄可能會在您更新記錄時暫時處於錯誤狀態。 也就是說,在您完成更新一個欄位之後,它可能會處於錯誤狀態,但在您開始更新下一個欄位之前。

若要防止過早限制式違規,您可以暫時暫停更新條件約束。 這有兩個用途:

  • 在您完成更新一個數據行但尚未開始更新另一個數據行之後,它可防止擲回錯誤。

  • 它可防止引發特定更新事件(經常用於驗證的事件)。

備註

在 Windows Forms 中,Datagrid 內建的數據系結架構會暫停條件約束檢查,直到焦點移出數據列,而且您不需要明確呼叫 BeginEditEndEditCancelEdit 方法。

在數據集上叫用 方法時 Merge ,會自動停用條件約束。 當合併完成時,如果資料集上有任何條件約束無法啟用,則會拋出 ConstraintException。 在這裡情況下, EnforceConstraints 屬性會設定為 false, ,而且必須先解決所有條件約束違規,才能將 EnforceConstraints 屬性 true重設為 。

完成更新之後,您可以重新啟用條件約束檢查,這不僅會重新啟用更新事件,還會引發相關事件。

如需暫停事件的詳細資訊,請參閱 在填滿數據集時關閉條件約束

數據集更新錯誤

當您更新數據集中的記錄時,可能會發生錯誤。 例如,您可能會不小心將錯誤類型的數據寫入數據行,或數據太長,或有其他完整性問題的數據。 或者,您可能有應用程式特定的驗證檢查,可在更新事件的任何階段引發自定義錯誤。 如需詳細資訊,請參閱 驗證資料集中的資料

維護變更的相關資訊

數據集中變更的相關信息會以兩種方式進行維護:藉由標記數據列,指出它們已變更 (RowState),以及保留記錄的多個複本 (DataRowVersion)。 藉由使用這項資訊,進程可以判斷數據集中已變更的內容,並可傳送適當的更新至數據源。

RowState 屬性

對象的 RowState 屬性是一個 DataRow 值,提供特定數據列狀態的相關信息。

下表詳細說明列舉的 DataRowState 可能值:

DataRowState 值 說明
Added 這列已作為一個項目新增到 DataRowCollection。 (此狀態中的列沒有對應的原始版本,因為在上次呼叫 AcceptChanges 方法時,它並不存在。)
Deleted 某行已使用 DataRow 物件的 Delete 刪除。
Detached 數據列已建立,但不屬於任何DataRowCollection的部分。 DataRow物件在建立之後立即處於這個狀態,在加入至集合之前,以及從集合中移除它之後。
Modified 數據列中的數據行值已以某種方式變更。
Unchanged 自上次呼叫 AcceptChanges 之後,列尚未變更。

DataRowVersion 列舉

數據集會維護多個版本的記錄。 在使用 DataRowVersion 物件的 Item[] 屬性或 GetChildRows 方法擷取 DataRowDataRow 所找到的值時,會用到這些欄位。

下表詳細說明列舉的 DataRowVersion 可能值:

DataRowVersion 值 說明
Current 記錄的目前版本包含自上次 AcceptChanges 呼叫以來已在記錄上執行的所有修改。 如果數據列已刪除,則沒有目前的版本。
Default 記錄的預設值,如數據集架構或數據源所定義。
Original 記錄的原始版本是記錄在數據集中最後一次認可變更後的狀態的複本。 實際上,這通常是從數據源讀取的記錄版本。
Proposed 暫時可用的記錄提案版本是在更新過程中產生的,即您在呼叫 BeginEdit 方法與 EndEdit 方法之間的時間。 您通常可以在事件處理程式中存取建議的記錄版本,例如 RowChanging。 調用 CancelEdit 方法會還原變更,並刪除建議的資料列版本。

當更新資訊傳送至數據源時,原始版本和目前版本很有用。 一般而言,當更新傳送至數據源時,資料庫的新資訊會位於記錄的目前版本。 來自原始版本的信息可用來找出要更新的記錄。

例如,在記錄的主鍵變更的情況下,您需要在數據源中找出正確的記錄,才能更新變更。 如果沒有原始版本存在,則記錄很可能被新增到數據源,不僅會導致多餘的記錄,還會出現一筆不正確且過期的記錄。 這兩個版本也會用於並行控制。 您可以將原始版本與數據源中的記錄進行比較,以判斷記錄在載入數據集後是否已經變更。

當您在實際認可數據集變更之前,需要執行驗證時,建議的版本會很有用。

即使記錄已變更,該數據列不一定有原始或目前的版本。 當您將新數據列插入數據表時,沒有原始版本,只有目前的版本。 同樣地,如果您藉由呼叫數據表的 Delete 方法來刪除數據列,則有原始版本,但沒有目前的版本。

您可以藉由查詢資料列的 HasVersion 方法來測試是否有特定版本的記錄存在。 當您查詢欄位的值時,可以將列舉值作為選用參數傳遞,以存取該記錄的任一版本。

獲取更改後的記錄

通常不要更新數據集中的每個記錄。 例如,使用者可能會使用顯示許多記錄的 Windows Forms DataGridView 控制件。 不過,使用者可能只會更新一些記錄、刪除一筆記錄,然後插入新的記錄。 數據集和數據表會提供方法 (GetChanges) 只傳回已修改的數據列。

您可以使用資料表 () 或資料集 (GetChangesGetChanges) 本身的 方法來建立已變更記錄GetChanges的子集。 如果您呼叫數據表的 方法,它會傳回只有變更記錄的數據表複本。 同樣地,如果您在數據集上呼叫 方法,您會取得只有變更記錄的新數據集。

GetChanges 本身會傳回所有已變更的記錄。 相反地,藉由將所需的 DataRowState 作為參數傳遞至 GetChanges 方法,您可以指定您想要的變更記錄子集:新增的記錄、標示為刪除的記錄、已分離的記錄或已修改的記錄。

當您想要將記錄傳送至另一個元件進行處理時,取得已變更記錄的子集很有用。 您可以只取得元件所需的記錄,來減少與其他元件通訊的額外負荷,而不是傳送整個數據集。

提交數據集中的變更

在數據集中進行變更時, RowState 會設定已變更數據列的 屬性。 原始和目前的記錄版本會由 RowVersion 屬性建立、維護及提供您。 儲存在這些已變更資料列屬性中的元數據,是傳送正確更新至資料來源所必需的。

如果變更反映數據源的目前狀態,您就不再需要維護這項資訊。 一般而言,數據集及其來源同步處理時有兩次:

  • 將資訊載入數據集後立即,例如從來源讀取數據時。

  • 將變更從數據集傳送至數據源之後(但不是之前,因為您會遺失將變更傳送至資料庫所需的變更資訊)。

您可以呼叫 AcceptChanges 方法,將待處理的變更提交至數據集。 通常, AcceptChanges 會在下列時間呼叫:

  • 載入數據集之後。 如果您藉由呼叫 TableAdapter 的 Fill 方法來載入數據集,配接器會自動為您提交變更。 不過,如果您通過將另一個數據集合併到其中來載入數據集,則必須手動認可變更。

    備註

    當您呼叫 Fill 方法時,可以透過將配接器的 AcceptChangesDuringFill 屬性設定為 false,以防止配接器自動認可變更。 如果設定為 false,則在填充期間插入的每個資料列的 RowState 會設定為 Added

  • 將數據集變更傳送至另一個程序之後,例如 XML Web 服務。

    謹慎

    以這種方式認可變更會清除任何變更資訊。 在您完成所有需要應用程式確認數據集變更的操作之前,請勿認可變更。

此方法會完成下列作業:

方法 AcceptChanges 可在三個層級使用。 您可以在 物件上 DataRow 呼叫它,只認可該數據列的變更。 您也可以在DataTable物件上呼叫它,以認可數據表中的所有行。 最後,您可以在 DataSet 物件上呼叫此方法,以提交數據集中所有資料表的所有記錄中的所有掛起變更。

下表描述根據呼叫方法的對象來認可哪些變更:

方法 結果
System.Data.DataRow.AcceptChanges 變更只會在特定行上提交。
System.Data.DataTable.AcceptChanges 特定數據表中的所有行都會提交變更。
System.Data.DataSet.AcceptChanges 變更會提交到數據集中所有數據表的所有數據行。

備註

如果您藉由呼叫 TableAdapter 的 Fill 方法來載入數據集,則不需要明確接受變更。 根據預設, Fill 方法會在完成填入數據表之後呼叫 AcceptChanges 方法。

相關的方法 RejectChanges 會藉由將 Original 版本複製回 Current 記錄的版本,來撤銷變更的效果。 它也會將每個記錄的 設定 RowStateUnchanged

資料驗證

若要確認應用程式中的數據符合傳遞至的進程需求,您通常必須新增驗證。 這可能牽涉到檢查表單中的用戶輸入是否正確、驗證由另一個應用程式傳送至你的應用程式的數據,或甚至檢查在元件內計算出的資訊是否符合數據來源及應用程式需求的限制條件。

您可以透過數種方式來驗證資料:

  • 在商務層中,藉由將程式代碼新增至應用程式來驗證數據。 數據集是一個您可以執行此動作的地方。 數據集提供後端驗證的一些優點,例如在數據行和數據列值變更時驗證變更的能力。 如需詳細資訊,請參閱 驗證資料集中的資料

  • 在呈現層中,藉由將驗證新增至表單。 如需詳細資訊,請參閱 Windows Forms 中的使用者輸入驗證

  • 在數據後端中,將數據傳送至數據源,例如資料庫,並允許它接受或拒絕數據。 如果您使用的資料庫具有複雜的數據驗證功能並提供錯誤資訊,這可以是實用的方法,因為不論數據來自何處,您都可以驗證數據。 不過,此方法可能無法容納應用程式特定的驗證需求。 此外,讓數據源驗證數據可能會導致對數據源進行多次來回行程,視應用程式如何協助解決後端所引發的驗證錯誤而定。

    這很重要

    使用數據命令搭配 CommandType 設定為 Text的屬性時,請先仔細檢查從用戶端傳送的資訊,再將它傳遞至您的資料庫。 惡意使用者可能會嘗試傳送已修改或額外的 SQL 語句,以取得未經授權的存取或損害資料庫。 將使用者輸入傳送至資料庫之前,請一律確認資訊是否有效。 最佳做法是盡可能一律使用參數化查詢或預存程式。

將更新傳輸至數據源

在數據集中變更之後,您可以將變更傳輸至數據源。 最常見的做法是呼叫 TableAdapter(或資料配接器)的 Update 方法。 方法會迴圈查看數據表中的每個記錄、判斷所需的更新類型(更新、插入或刪除),如果有的話,然後執行適當的命令。

說明如何進行更新,假設您的應用程式使用包含單一數據表的數據集。 應用程式會從資料庫擷取兩個數據列。 擷取之後,記憶體內部數據表看起來像這樣:

(RowState)     CustomerID   Name             Status
(Unchanged)    c200         Robert Lyon      Good
(Unchanged)    c400         Nancy Buchanan    Pending

您的應用程式會將 Nancy Buchanan 的狀態變更為「慣用」。由於這項變更,該資料列的 RowState 屬性值會從 Unchanged 變更為 Modified。 第一個數據列的 RowState 屬性值維持 Unchanged不變。 數據表現在看起來像這樣:

(RowState)     CustomerID   Name             Status
(Unchanged)    c200         Robert Lyon      Good
(Modified)     c400         Nancy Buchanan    Preferred

您的應用程式現在會呼叫 Update 方法,將數據集傳輸至資料庫。 方法會接著檢查每個數據列。 對於第一個數據列,方法不會將 SQL 語句傳送至資料庫,因為該數據列自原本從資料庫擷取后尚未變更。

不過,對於第二個數據列, Update 方法會自動叫用正確的數據命令,並將其傳輸至資料庫。 SQL 語句的特定語法取決於基礎數據存放區支援的 SQL 方言。 但是,下列傳輸 SQL 語句的一般特性值得注意:

  • 傳輸的 SQL 語句是 UPDATE 語句。 配接器知道使用 UPDATE 語句,因為 屬性的值 RowStateModified

  • 傳輸的 SQL 語句包含 WHERE 子句,指出 語句的目標 UPDATE 為 其中 CustomerID = 'c400'的數據列。 語句的 SELECT 這個部分會區分目標數據列與所有其他數據列,因為 CustomerID 是目標數據表的主鍵。 子句的資訊 WHERE 衍生自記錄的原始版本 (DataRowVersion.Original),以防識別數據列所需的值已變更。

  • 傳輸的 SQL 語句包含 SET 子句,可設定修改之數據行的新值。

    備註

    如果 TableAdapter 的 UpdateCommand 屬性已設定為預存程式的名稱,配接器就不會建構 SQL 語句。 相反地,它會使用傳入的適當參數叫用預存程式。

傳遞參數

您通常會使用參數來傳遞資料庫中即將更新之記錄的值。 當 TableAdapter 的 Update 方法執行 UPDATE 語句時,它必須填入參數值。 它會從 Parameters 集合中取得適用於資料命令的這些值,在這種情況下,該物件位於 TableAdapter 中的 UpdateCommand

如果您使用 Visual Studio 工具來產生數據配接器,物件 UpdateCommand 會包含對應至 語句中每個參數佔位元的參數集合。

System.Data.SqlClient.SqlParameter.SourceColumn每個參數的 屬性會指向數據表中的數據行。 例如,SourceColumnOriginal_au_id 參數的 au_id 屬性會設定為數據表中任何數據行包含作者標識碼。當配接器Update的方法執行時,它會從要更新的記錄讀取作者標識碼數據行,並將值填入語句中。

UPDATE在語句中,您必須指定新的值(將寫入記錄的值),以及舊值(以便記錄可以位於資料庫中)。 因此,每個值都有兩個參數:一個用於 SET 子句,另一個用於 WHERE 子句。 這兩個參數都會從要更新的記錄讀取數據,但會根據參數的 SourceVersion 屬性取得不同版本的數據行值。 子句的參數 SET 會取得目前的版本,而 子句的參數 WHERE 會取得原始版本。

備註

您也可以在程式代碼中 Parameters 自行設定集合中的值,這通常會在數據配接器 RowChanging 事件的事件處理程式中執行。