用SqlDataAdapter.Update(DataSet Ds)更新資料庫

來源:互聯網
上載者:User

一. 用SqlDataAdapter.Update(DataSet Ds)更新資料庫.
1. DbDataAdapter調用 Update 方法時,DataAdapter 將分析已作出的更改並執行相應的命令(INSERT、UPDATE 或 DELETE)。當 DataAdapter 遇到對 DataRow 的更改時,它將使用 InsertCommand、UpdateCommand 或 DeleteCommand 來處理該更改。這樣,您就可以通過在設計時指定命令文法並在可能時通過使用預存程序來盡量提高 ADO.NET 應用程式的效能。在調用 Update 之前,必須顯式設定這些命令。如果調用了 Update 但不存在用於特定更新的相應命令(例如,不存在用於已刪除行的 DeleteCommand),則將引發異常。
但是如果 DataTable 映射到單個資料庫表或從單個資料庫表產生,則可以利用 CommandBuilder 對象自動產生 DataAdapter 的 DeleteCommand、InsertCommand 和 UpdateCommand。為了自動產生命令,必須設定 SelectCommand 屬性,這是最低的要求。SelectCommand 所檢索的表架構 確定自動產生的 INSERT、UPDATE 和 DELETE 語句的文法。如果在自動產生插入、更新或刪除命令後修改 SelectCommand 的 CommandText,則可能會發生異常。如果已修改的 SelectCommand.CommandText 所包含的架構資訊與自動產生插入、更新或刪除命令時所使用的 SelectCommand.CommandText 不一致,則以後對 DataAdapter.Update 方法的調用可能會試圖訪問 SelectCommand 引用的當前表中已不存在的列,並且會引發異常。可以通過調用 CommandBuilder 的 RefreshSchema 方法來重新整理 CommandBuilder 用來自動產生命令的架構資訊。
對於DbDataAdapter.Update 方法更新資料庫,每次在調用DbDataAdapter.Update(ds) 之後一定要ds.acceptchanges
否則會對後面用到的ds出現意想不到的錯誤。
鬱悶~ 改了一天的程式,才發現是這個錯誤,太鬱悶了~~
2. SqlCommandBuilder會自動產生更新、修改、刪除的sql語句,進行更新。
3.當表中沒有主鍵時,自動產生的SQL語句進行UPDate時,會出現異常資訊。
異常資訊為:用SqlCommandBuilder更新DataSet,遇到“對於不返回任何鍵列資訊的 SelectCommand 不支援 UpdateCommand 的動態 SQL 產生”問題。
4.解決辦法:1. 修改表的定義,定義一個主鍵;
2. 為SqlDataAdapter指定UpdateCommand(DeleteCommand,InsertCommand應該也一樣);

例子:

1.string emailSql="select email,validFlag from emailMe";
DataSet emailAdd=new DataSet();
SqlDataAdapter emailAdapter=new SqlDataAdapter(emailSql,myConn);
SqlCommandBuilder cb=new SqlCommandBuilder(emailAdapter);
emailAdapter.Fill(emailAdd,"address");
myConn.Close();
DataTable myDt=emailAdd.Tables["address"];
myDt.PrimaryKey=new DataColumn[]{myDt.Columns["email"]};
......//修改myDs資料
emailAdapter.Update(emailAdd,"address");

自動產生SQL語句。

2. string emailSql="select email,validFlag from emailMe";
DataSet emailAdd=new DataSet();
SqlDataAdapter emailAdapter=new SqlDataAdapter(emailSql,myConn);
SqlCommandBuilder cb=new SqlCommandBuilder(emailAdapter);
SqlCommand upCmd=new SqlCommand("update ["+strTableName+"] set validFlag=@validFlag where email=@email",myConn);
upCmd.Parameters.Add("@validFlag",SqlDbType.Int,8,"validFlag");
upCmd.Parameters.Add("@email",SqlDbType.NVarChar,100,"email");
emailAdapter.UpdateCommand=upCmd;
emailAdapter.Fill(emailAdd,"address");
myConn.Close();
......//修改myDs資料
emailAdapter.Update(emailAdd,"address");

自訂SQL語句。

二.檢索“標識”或“自動編號”值
檢索“標識”或“自動編號”值
此頁面僅適用於
Microsoft Visual Studio 2005/.NET Framework 2.0

同時提供下列產品的其他版本:
Microsoft Visual Studio 2008/.NET Framework 3.5
.NET Framework 開發人員指南
檢索“標識”或“自動編號”值
為了確保表中的每一行都有唯一的值,可以將 DataTable 中的列設定為自動遞增的主鍵。但是,您的應用程式可能會有多個用戶端,而每個用戶端都可能會使用一個單獨的 DataTable 執行個體。在這種情況下,單獨的 DataTable 執行個體之間最終可能會出現重複的值。由於所有用戶端都使用單個資料來源,因此可以讓資料來源定義自動遞增值,從而解決這一衝突。若要完成此任務,請使用 Microsoft SQL Server 中的“標識”列或 Microsoft Access 中的“自動編號”欄位。

如果使用資料來源為添加到 DataSet 中的新行填充“標識”或“自動編號”列,則會出現唯一的情況,因為 DataSet 與資料來源之間沒有直接連接。因此,DataSet 不識別任何由資料來源自動產生的值。但是,對於可以建立帶有輸出參數的預存程序的資料來源(如 Microsoft SQL Server),可以將自動產生的值(如新的標識值)指定為輸出參數並使用 DataAdapter 將該值對應回 DataSet 中的相應列。

資料來源可能不支援帶有輸出參數的預存程序。在這種情況下,將可以使用 RowUpdated 事件來檢索自動產生的值,並將其放入 DataSet 中的插入行或更新行。本節包含一個樣本,顯示當在 Microsoft Access 2000 或更高版本上使用 Jet 4.0 OLE DB 提供者時,如何將代碼添加到 RowUpdated 事件中以確定插入是否已發生,然後檢索自動產生的值並將其儲存在當前更新的行中。

檢索 SQL Server“標識”列的值

以下預存程序和程式碼範例顯示如何將自動遞增的標識值從 Microsoft SQL Server 表映射回添加到向 DataSet 的表添加的行中的相應列。該預存程序用於將一個新行插入 Northwind 資料庫的 Categories 表並以輸出參數的形式返回從 Transact-SQL SCOPE_IDENTITY() 函數返回的標識值。

複製代碼
CREATE PROCEDURE InsertCategory
@CategoryName nchar(15),
@Identity int OUT
AS
INSERT INTO Categories (CategoryName) VALUES(@CategoryName)
SET @Identity = SCOPE_IDENTITY()
InsertCategory 預存程序可以指定為 InsertCommand 的源。為接收“標識”輸出參數建立一個參數。該參數具有 Output 的 ParameterDirection,並將 SourceColumn 指定為 DataSet 中本地 Categories 表的 CategoryID 列。為添加的行處理 InsertCommand 後,將自動遞增的標識值作為此輸出參數返回,然後放入當前行的 CategoryID 列中。

以下程式碼範例顯示如何以輸出參數的形式返回自動遞增的值並將其指定為 DataSet 中 CategoryID 列的源值。

Visual Basic
複製代碼
' Assumes that connection is a valid SqlConnection object.
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")

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#
複製代碼
// Assumes that connection is a valid SqlConnection object.
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“自動編號”值

Microsoft Access 不支援預存程序或批命令處理,因此無法將輸出參數映射到上例所示表中的源列。但是,Microsoft Access 2000 或更高版本支援 @@IDENTITY 屬性在“插入”(INSERT) 後檢索“自動編號”欄位的值。使用 RowUpdated 事件,您可以確定“插入”(INSERT) 是否已發生,檢索最新的“自動編號”值,然後將該值放入 DataSet 中本地表的“標識”列。

以下程式碼範例顯示如何使用 OleDbDataAdapter 將一個新值插入 Microsoft Access 2000 Northwind 資料庫的 Categories 表中。該樣本使用 RowUpdated 事件來填充在向 Categories 表中插入記錄時 Jet 引擎和 Access 資料庫所產生的“自動編號”值。請注意,這僅適用於 Jet 4.0 OLE DB 提供者和 Microsoft Access 2000 或更高版本。

Visual Basic
複製代碼
' Assumes that connection is a valid OleDbConnection object.
' Use the DataAdapter to fill and update the DataSet.
Dim adapter As OleDbDataAdapter = 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()

' Fill the DataSet.
Dim categories As DataSet = New DataSet
adapter.Fill(categories, "Categories")

' Add a new row.
Dim newRow As DataRow = categories.Tables("Categories").NewRow()
newRow("CategoryName") = "New Category"
categories.Tables("Categories").Rows.Add(newRow)

' Include an event to fill in the Autonumber value.
AddHandler adapter.RowUpdated, _
New OleDbRowUpdatedEventHandler(AddressOf OnRowUpdated)

' Update the DataSet.
adapter.Update(categories, "Categories")
connection.Close()

' Event procedure for OnRowUpdated
Private Shared Sub OnRowUpdated( _
sender As Object, args As OleDbRowUpdatedEventArgs)
' Include a variable and a command to retrieve the identity value
' from the Access database.
Dim newID As Integer = 0
Dim idCMD As OleDbCommand = New OleDbCommand( _
"SELECT @@IDENTITY", connection)

If args.StatementType = StatementType.Insert
' Retrieve the identity value and store it in the CategoryID column.
newID = CInt(idCMD.ExecuteScalar())
args.Row("CategoryID") = newID
End If
End Sub
C#
複製代碼
// Assumes that connection is a valid OleDbConnection object.
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();

// Fill the DataSet.
DataSet categories = new DataSet();
adapter.Fill(categories, "Categories");

// Add a new row.
DataRow newRow = categories.Tables["Categories"].NewRow();
newRow["CategoryName"] = "New Category";
categories.Tables["Categories"].Rows.Add(newRow);

// Include an event to fill in the Autonumber value.
adapter.RowUpdated += new OleDbRowUpdatedEventHandler(OnRowUpdated);

// Update the DataSet.
adapter.Update(categories, "Categories");

connection.Close();

// Event procedure for OnRowUpdated
protected static void OnRowUpdated(
object sender, OleDbRowUpdatedEventArgs args)
{
// Include a variable and a command to retrieve the identity value from the Access database.
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;
}
三.使用 DataAdapter 和 DataSet 更新資料庫 [C#]
DataAdapter 的 Update 方法可調用來將 DataSet 中的更改解析回資料來源。與 Fill 方法類似,Update 方法將 DataSet 的執行個體和可選的 DataTable 對象或 DataTable 名稱用作參數。DataSet 執行個體是包含已作出的更改的 DataSet,而 DataTable 標識從其中檢索更改的表。

當調用 Update 方法時,DataAdapter 將分析已作出的更改並執行相應的命令(INSERT、UPDATE 或 DELETE)。當 DataAdapter 遇到對 DataRow 的更改時,它將使用 InsertCommand、UpdateCommand 或 DeleteCommand 來處理該更改。這樣,您就可以通過在設計時指定命令文法並在可能時通過使用預存程序來盡量提高 ADO.NET 應用程式的效能。在調用 Update 之前,必須顯式設定這些命令。如果調用了 Update 但不存在用於特定更新的相應命令(例如,不存在用於已刪除行的 DeleteCommand),則將引發異常。

Command 參數可用於為 DataSet 中每個已修改行的 SQL 陳述式或預存程序指定輸入和輸出值。有關更多資訊,請參閱將參數用於 DataAdapter。

如果 DataTable 映射到單個資料庫表或從單個資料庫表產生,則可以利用 CommandBuilder 對象自動產生 DataAdapter 的 DeleteCommand、InsertCommand 和 UpdateCommand。有關更多資訊,請參閱自動產生的命令。

Update 方法會將更改解析回資料來源,但是自上次填充 DataSet 以來,其他用戶端可能已修改了資料來源中的資料。若要使用當前資料重新整理 DataSet,請再次使用 DataAdapter 填充 (Fill) DataSet。新行將添加到該表中,更新的資訊將併入現有行。

若要處理可能在 Update 操作過程中發生的異常,可以使用 RowUpdated 事件在這些異常發生時響應行更新錯誤(請參閱使用 DataAdapter 事件),或者可以在調用 Update 之前將 DataAdapter.ContinueUpdateOnError 設定為 true,然後在 Update 完成時響應儲存在特定行的 RowError 屬性中的錯誤資訊(請參閱添加和讀取行錯誤資訊)。

注意 如果對 DataSet、DataTable 或 DataRow 調用 AcceptChanges,則將使某 DataRow 的所有 Original 值被該 DataRow 的 Current 值改寫。如果已修改將該行標識為唯一行的欄位值,那麼當調用 AcceptChanges 後,Original 值將不再匹配資料來源中的值。

以下樣本示範如何通過顯式設定 DataAdapter 的 UpdateCommand 來執行對已修改行的更新。請注意,在 UPDATE 語句的 WHERE 子句中指定的參數設定為使用 SourceColumn 的 Original 值。這一點很重要,因為 Current 值可能已被修改,並且可能不匹配資料來源中的值。Original 值是曾用來從資料來源填充 DataTable 的值。

SqlClient

[Visual Basic]

Dim catDA As SqlDataAdapter = New SqlDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)

catDA.UpdateCommand = New SqlCommand("UPDATE Categories SET CategoryName = @CategoryName " & _

"WHERE CategoryID = @CategoryID", nwindConn)

catDA.UpdateCommand.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15, "CategoryName")

Dim workParm As SqlParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", SqlDbType.Int)

workParm.SourceColumn = "CategoryID"

workParm.SourceVersion = DataRowVersion.Original

Dim catDS As DataSet = New DataSet

catDA.Fill(catDS, "Categories")

Dim cRow As DataRow = catDS.Tables("Categories").Rows(0)

cRow("CategoryName") = "New Category"

catDA.Update(catDS)

[C#]

SqlDataAdapter catDA = new SqlDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);

catDA.UpdateCommand = new SqlCommand("UPDATE Categories SET CategoryName = @CategoryName " +

"WHERE CategoryID = @CategoryID" , nwindConn);

catDA.UpdateCommand.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15, "CategoryName");

SqlParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", SqlDbType.Int);

workParm.SourceColumn = "CategoryID";

workParm.SourceVersion = DataRowVersion.Original;

DataSet catDS = new DataSet();

catDA.Fill(catDS, "Categories");

DataRow cRow = catDS.Tables["Categories"].Rows[0];

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

catDA.Update(catDS);

OleDb

[Visual Basic]

Dim catDA As OleDbDataAdapter = New OleDbDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)

catDA.UpdateCommand = New OleDbCommand("UPDATE Categories SET CategoryName = ? " & _

"WHERE CategoryID = ?" , nwindConn)

catDA.UpdateCommand.Parameters.Add("@CategoryName", OleDbType.VarChar, 15, "CategoryName")

Dim workParm As OleDbParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", OleDbType.Integer)

workParm.SourceColumn = "CategoryID"

workParm.SourceVersion = DataRowVersion.Original

Dim catDS As DataSet = New DataSet

catDA.Fill(catDS, "Categories")

Dim cRow As DataRow = catDS.Tables("Categories").Rows(0)

cRow("CategoryName") = "New Category"

catDA.Update(catDS)

[C#]

OleDbDataAdapter catDA = new OleDbDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);

catDA.UpdateCommand = new OleDbCommand("UPDATE Categories SET CategoryName = ? " +

"WHERE CategoryID = ?" , nwindConn);

catDA.UpdateCommand.Parameters.Add("@CategoryName", OleDbType.VarChar, 15, "CategoryName");

OleDbParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", OleDbType.Integer);

workParm.SourceColumn = "CategoryID";

workParm.SourceVersion = DataRowVersion.Original;

DataSet catDS = new DataSet();

catDA.Fill(catDS, "Categories");

DataRow cRow = catDS.Tables["Categories"].Rows[0];

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

catDA.Update(catDS);

自動遞增列

如果來自資料來源的表包含自動遞增列,則可以使用由資料來源產生的值填充 DataSet 中的列,方法是通過以預存程序輸出參數的形式返回自動遞增值並將其映射到表中的一列,或者使用 DataAdapter 的 RowUpdated 事件。有關樣本,請參閱檢索“標識”或“自動編號”值。

但是,DataSet 中的值可能會與資料來源中的值不同步並導致意外的行為。例如,請考慮一個包含自動遞增主鍵列 CustomerID 的表。如果在該 DataSet 中添加兩個新客戶,它們將收到自動遞增的 CustomerId 值 1 和 2。在向 DataAdapter 的 Update 方法傳遞第二個客戶行時,新添加的行會收到資料來源中的自動遞增 CustomerID 值 1,該值與 DataSet 中的值 2 不匹配。當 DataAdapter 使用傳回值填充 DataSet 中的行時,由於第一個客戶行的 CustomerID 已經是 1,因此將發生約束衝突。

為了避免這種行為,建議在使用資料來源中的自動遞增列和 DataSet 中的自動遞增列時,在 DataSet 中建立 AutoIncrementStep 為 -1 且 AutoIncrementSeed 為 0 的列,並確保資料來源產生從 1 開始並以正步長值遞增的自動遞增標識值。這樣,DataSet 將為自動遞增值產生負數,這些負數不會與資料來源所產生的正自動遞增值發生衝突。另一種方法是使用 Guid 類型的列而不是自動遞增列。產生 Guid 值的演算法在 DataSet 中產生的 Guid 從不會與資料來源產生的 Guid 相同。有關定義 DataTable 中的列的更多資訊,請參閱定義資料表的架構。

插入、更新和刪除的排序

在許多情況下,以何種順序向資料來源發送通過 DataSet 作出的更改是相當重要的。例如,如果已更新現有行的主索引值並且添加了具有新主索引值的新行,則務必要在處理插入之前處理更新。

可以使用 DataTable 的 Select 方法來返回僅引用具有特定 RowState 的 DataRow 數組。然後可以將返回的 DataRow 數組傳遞到 DataAdapter 的 Update 方法來處理已修改的行。通過指定要更新的行的子集,可以控制處理插入、更新和刪除的順序。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.