用SqlDataAdapter.Update(DataSet Ds)更新数据库

一. 用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 方法来处理已修改的行。通过指定要更新的行的子集,可以控制处理插入、更新和删除的顺序。

posted @ 2010-03-24 14:31  与时俱进  阅读(5995)  评论(1编辑  收藏  举报
友情链接:同里老宅院民居客栈