使用DataSet更新记录
ADO.NET的DataSet对象提供了更好的编程实现数据库更新的功能。因为DataSet对象与数据库始终是不连接的,可以
添加、修改和删除DataSet对象中包含的任何脱离数据库连接的记录。在完成他修改了之后,可以通过将
DataSet对象链接到DataAdapter对象来记录传输给数据库。
首先,假定已经用Northwind 数据库中的Products表中的记录填充了DataSet,就像下面的代码片断所演示的那样:
String strSQL="SELECT ProductID,ProductName,UnitPrice FROM Products";
SqlDataAdapter adapter=new SqlDataAdatpter(strSQL,objConnection);
DataSet ds=new DataSet()
adapter.Fill(ds,ProductTable);
在上述代码的最后一行,我们将保存这些记录的DataSet中表的名称命名为ProductTable,但是通常该表的名称可以
按照您的喜好来进行指定--当然可以不必使它的名称和数据库中表的名称相同。该特性的优点是您可以很容易将
内存中表的表示数据和它在数据库中的源数据区分开。至于涉及到的应用程序,您总是处理名称为ProductTable 表,
如下代码所示:
DataTable tbl=ds.Tables[ProductTable];
(事实上,绝大多数应用程序都是使用存储过程来执行数据库操作的,因此数据库管理可以修改所有引用表的存储过程,
而根本不需国修改代码。)
说明:在得到DataSet之后,应当始终尽可能快地释放到数据库连接,方法是通过调用Connection对象的Close() 方法。
1.修改DataSet中的记录
使用手头的DataSet对象,可以修改其中包含的一条或多条记录。下面的代码片断说明了如何改变产品的价格:
DataTable tbl=ds.Tables[ProductTable];
tbl.PrimaryKey=new DataColumn[]{tbl.Columns["ProductID"]};
DataRow row=tbl.Rows.Find(1);
row.Item["UnitRrice"]=100;
这里需要提醒大家意识到的一个关键地方是,要想修改DataSet中的记录,我们首先需要查找到该记录。这是我们以前没
有完成的事情。该过程的第一步是将数据表中的一列(或选择几个列)作为表的主键码。然后可以将主键码作为表中行的
索引来使用,以便能够快速找到需要查找的记录。
正如在上文中所暗示的,有的时候使用单个列作为主键码。在其他的时候,可能使用几个列作为主键码。结果是将
DataTable.PrimaryKey属性指定为一起构成主键码的列集合,需要用所有相关的列来初始化主键码,就象在上述第二行代码中所显示
的那样。与之形成对照的是,如果主键码是由CategoryID列和ProductID列共同组成的,必须使用如下所示的代码来定义主键码:
tbl.PrimaryKey=new DataColumn[]{tbl.Columns["CategoryID"],tbl.Columns["ProductID"]}
继续介绍例子中下面的代码。DataRowCollection对象(由DataTable.Rows属性表示)的Find()方法接受主键码的值,并返回包含与主
键相匹配的键码的DataRow.如果主键码是由多个列组成,需要传入一组数值。例如,如果主键码是由CategoryID列和ProductID列组成的,
如下所示的代码将查找CategoryID值为1并且ProductID数值为3的记录行。
Object KeyValues(1);
KeyValues(0)=1;
KeyValues(1)=3;
DataRow row=tbl.Rows.Find(KeyValues);
查到记录之后,就可以修改记录中它表示的任何字段,只需为字段指定一个新值即可。在前面的例子中,通过将UnitPrice字段的值指定为
100来修改该字段。可以按照需要对该记录进行多处修改。也可以通过对记录进行搜索和执行与上述相同的操作步骤来修改更多的记录。
记住,这里进行的修改仅仅影响的是DataSet--数据库在这时还没有被更新。
2.更新数据库中的记录
要想您在DataSet对象中进行的修改放到数据库中,首先必须要连接到数据库中,然后使用(与您用来检索数据相同的)DataAdapter对象来
用新的数据信息更新数据库:
objConnection.Open()
adapter.Update(ds,ProductTable);
objConnection.Close();
正像在上述代码在所看到的,在将记录插入前一章创建的数据库时,通过对数据适配器的Update()方法的调用巧妙地完成了新数据信息传入数据库的
任务。然后,为了使该机制能够运转,我们必须立即建立一个CommandBuilder对象。这次幕后的工作会变得更加复杂,但是分析它究竟是怎么工作的
是一件有趣的事情。
ADO.NET通过检查在DataSet对象中的特定表(在该例子中是ProductTable)的所有记录来决定对数据库中的哪条记录进行更新。当最初用数据库中的记录
填充DataSet对象时,ADO.NET会为每个字段保存两个副本:初始值和当前值。如果需要的话,可以通过在表达式中指定需要的值来分别访问这两个值,
代码如下所示:
row.Item("UnitPrice",DataRowVersion.Original);
另外,每行都有一个名称为RowState的属性,该属性用于表示该行的当前状态。它可以是在System.Data.DataRowState命名空间中定义的5个枚举中的任一个
Added该行已经被添加到表中
Deleted该行已经从表中删除了。需要注意的是,这里删除是指该行仅仅被打上了删除标记,它还没有被实际删除。它就允许ADO.NET稍后再删除数据库中的
这些相应的行。
Detached该行不在表中。这种情况通常是,当创建了一个新的DataRow对象,但是还没有将它添加到表的Rows集合中或者是已经使用Romove()方法从表中删除了
该行。
Modififed该行已经被修改了。例如,您已经为该行中的一个字段指定了新的值。
Unchanged该行至今尚未变化。
当通过更改字段的值(就像我们在前面的例子中修改的UnitPrice字段)来修改记录时,ADO.net会将行的当前值更改为重新指定的值,并将 RowState属性值改为
Modified。然后,当调用数据适配器的 Update()方法时,ADO.NET会使用当前值来更新数据库中的相应字段。
如果需要的话,可以调用DataRow对象的AcceptChanges(0方法,来将所有字段的初始值改为当前值
(该方法还会将RowState属性的值设置为Unchanged)。或者也可以调用DataTable的AcceptChange(0方法将变化更新到表中的所有行中。
与AcceptChanges()方法作用相反的是RejectChanges()方法,它也同样对行和表这两个对象提供支持,作用是弃您已经对DataSet对象做出的全部修改。调用行对象
的RejectChanges()方法会将该行的RowState属性改回为Unchanged.如果调用表对象的RejectChanges()方法,会失去对所有行的改变,并且会将所有行的RowState属性
重新设置为Unchanged.调用表对象的RejectChanges()方法还会删除所有已经添加到表中的行。
使用CommandBuilder
是有帮助的。SqlCommandBuilder cb=new SqlCommandBuilder(adapter);
上述代码的结果是,SqlCommmandBuilder的构造函数会使用SQL UPDATE语句(基于适配器的SELECT命令)来创建一个SqlCommand对象,然后将该Command对象指定给数据适
配器的UpdateCommand属性。CommandBuilder类同样可以构建INSERT和DELETE命令,会在您创建实例时自动完成)
3.手工创建Update命令
虽然 CommandBuider对象的确能够完成Update命令的创建工作,并且能够为您省去许多编码工作,但是它们也有它们的缺点。至少它们存在着如下限制:
*它们只能用于更单个数据库表中的记录。如果您有一个需要从两个或两个以上的表中提取记录的查询,CommandBuilder将不能为您构建Update 命令。
*指定给DataAdapter对象的SelectCommand属性的SQL语句必须要返回一个包含惟一识别返回记录的值的列。
*如果SelectCommand属性变化了,必须调用适配器对象的RefreshSchema()方法来更新元数据,它用于生成Insert、Update 和Delete命令。
如果您的情况符合上述限制中的任何一条,CommandBuilder对象都不会为您工作,您就必须手工来构建Update命令
SqlCommand cmd=nw SqlCommand("UPDATE Products SET UnitPrice=@Price WHERE ProductID=@ProductID",objConnection)
SqlParameter param=cmd.Parameters.Add("@ProductID",SqlDbType.Int);
param.SourceColumn="ProductID";
param.SourceVersion=DataRowVersion.Original;
param=cmd.Parameters.Add("@Price",SqlDbType.Money);
param.SourceColumn="UnitPrice";
param.SourceVersion=DataRowVersion.Current;
adapter.UpdateCommand=cmd;
在上述代码中,我们创建一个Command对象并把它的构造函数传给完成更新操作的SQLUPDATE语句(或存储过程)。因为此时我们不知道要对哪条记录进行更新,以及更新后的
价格是多少,我们指定的ID和产品的价格稍后会由确定参数的语句来提供。
SQL语句必须包含您想要更新的表的每一列。如果您改变了一些字段的值,但是不包括UPDATE语句中有关列,不需要做任何改变来发现它们到数据库的踟径。
接着我们为命令增加两个参数。第一个参数是 @ProductID,它是整数类型的--我们指定它应当被绑定到表中的ProductI列,并且通过参数SourceVersion属性将参数绑定
到数据库中该字段的初始值来确保我们正在处理的是正确的行。第二个参数是@Price,它绑定到UnitPrice列,所包含的是数值的当前值。
完成了Command对象的定义以后,我们将其指定给 DataAdapter对象的UpdateCommand属性。当我们调用数据适配器的Update() 方法时,该命令会执行更新数据库中正确的产品
记录。
一个例子:
web.config中<configuration>
<appSettings>
<add key="strConnection" value="server=station-05\ggll;database=Northwind;uid=sa;pwd=sa" />
cs中
using System.Data.SqlClient;
using System.Configuration;
namespace sp
{