本文档由李欣蔚(nirvana_li)翻译自http://www.csharp-station.com/,转载请注名出处!
更新日期2006-2-14
Lesson 05: Working with Disconnected Data - The DataSet and SqlDataAdapter
第五课:使用断开数据--DataSet和SqlDataAdapter
这节课解释如何使用断开数据,使用DataSet和SqlDataAdapter对象。这里这节课的目标:
- 了解断开数据的需要
- 获得对于DataSet用来做什么的基本了解
- 学习如何使用SqlDataAdapter来找回和更新数据
介绍
在第三课中,我们讨论了使用Sqlcommand对象的与数据源的完全连接操作模型。在第四课,我们学习了如何通过连接使用SqlDataReader快速的读取数据。这节课介绍如何通过使用DataSet和SqlDataAdapter来实现SqlConnection和SqlDataReader交互之间的事情。
DataSet是存储在内存中的数据,它保存了大量的表。DataSet只存储数据而并不与数据源发生交互。它通过SqlDataAdapter来管理与数据源的连接并给予我们非连接的行为。只有当需要的时候,SqlDataAdapter打开连接并在完成任务后关闭它。比如,当Data填充数据的时候SqlDataAdapter执行以下的操作:
1. 打开连接
2. 将数据读入到DataSet中
3. 关闭连接
当更新DataSet时更新数据源,它执行以下的操作:
1. 打开连接
2. 将DataSet中的改变写入数据源
3. 关闭数据源
在Fill和Update操作中间的时间,数据源的连接是断开的,你能够随便的使用DataSet对数据进行读写。这些是使用断开数据的机制。因为只有当需要的时候应用程序才连接,这样应用程序才更加的可扩展。
一对场景描述了为什么你需要断开数据:人们使用网络连接并使web站点更加可扩展。考虑销售人员在旅行的时候需要客户数据。在最初的几天,他们将需要与主数据库同步以获得有效的最新信息。在这些天,它们将对已存在的用户数据进行修改,添加年用户,并且添加新的订单。这都是可以的,因为他们有相应的地区或者客户基础,而其它的用户不会改变相同的记录。在最后的几天中,销售人员将连接网络并把前晚上的改变的数据更新。
另一个场景是使web站点更加可扩展。使用SqlDataReader,你必须在显示每一页的时候返回数据库读取记录。则需要对每一次页面装载重新读取数据库,这将在用户数量增加的时候引起麻烦。消除这种情况的一种办法是使用DataSet,它只更新一次并缓存数据。每一个对页面的查询都会检测缓存并显示它。这避免的数据库的连接,使你的应用程序更加有效率。
上面的场景的异常包括你需要更新数据的情况。你必须抉择,基于你的场景中的数据如何被使用。当你的信息主要是读,则使用断开数据,但是当你需要更加动态的调用某些东西的时候,考虑其它的可选项(比如使用SqlCommand对象直接更新)。实际上,万事难料,但真正的指导方针应该是应用程序的需求,它将影响你的设计。
创建DataSet对象
实例化DataSet没有任何特别的内容,你应该创建一个新的实例,就像其它对象一样:
DataSet dsCustomers = new DataSet();
DataSet构造函数并不需要参数。然而当你将数据序列化为XML的时候为了得到DataSet的名字却有额外的负担。因为在这个例子中并不需要,我就不提了。现在数据集为空并且你需要SqlDataAdapter来装载它。
创建SqlDataAdapter
SqlDataAdapter使用SQL命令和连接对象来读写数据。对它进行初始化使用SQL选择语句和连接对象。
SqlDataAdapter daCustomers = new SqlDataAdapter(
"select CustomerID, CompanyName from Customers", conn);
上面的代码创建了一个新的SqlDataAdapter对象daCustomers,SQL的select语句指明了将哪些数据读入数据集。连接对象conn应该已经被实例化,但不用打开。这是由SqlDataAdapter在调用Fill和Update方法的时候负责打开和关闭连接的。
正如早先指明的,SqlDataAdapter包含所有必需的与数据源交互的命令。代码显示了如何指定select语句,但是并没有显示insert,update和delete语句。这些在初始化以后添加到SqlDataAdapter中。
这里有两种方式添加insert,update和delete命令:通过SqlDataAdapter属性或者通过SqlCommandBuilder.在这节课,我将介绍使用SqlCommandBuilder的简单方式。在接下来的课程中,我将介绍如何使用SqlDataAdapter属性,它将需要更多的工作但是会带来比SqlCommandBuilder更大的能力。下面是如何使用SqlCommandBuilder在SqlDataAdapter中添加命令:
SqlCommandBuilder cmdBldr = new SqlCommandBuilder(daCustomers);
注意上面的代码中SqlCommandBuilder是使用一个SqlDataAdapter对象daCustomers作为参数的构造函数实例化的。这说明SqlCommandBuilder对哪一个SalDataAdapter添加命令。SqlCommandBuilder将读取SQL的select语句(在SqlDataAdapter被实例化的时候指明),推断inser,update和delete命令,并将新的命令分别赋值给SqlDataAdapter的Insert,Update和Delete属性。
正如我先前所说,SqlCommandBuilder有限制性。它能在你对单独的表做简单的select语句的时候有效。然而,当需要连接两个以上的表或者必须执行一个存储过程的时候,它不会起作用。我将在以后的课程中描述这些场景的工作区。
填充DataSet
当具有一个DataSet和SqlDataAdapter实例以后,你需要填充数据集。下面是如何实现它,只要使用SqlDataAdapter的Fill方法:
daCustomers.Fill(dsCustomers, "Customers");
上面代码中的Fill方法具有两个参数:DataSet对象和表名。DataSet必须在填充数据之前被实例化。第二个参数是将要在DataSet中创建的表的名字。你能够以你希望的任何名字对表命名。这要求能够用一个有意义的名字来标识表,以便以后使用。通常,我使用与数据库中表相同的名字来命名。然而,如果SqlDataAdapter的select命令包含多表连接,你将需要另外找一个有意义的名字。
Fill方法有另外一个重载的方式,它只接受DataSet一个参数。在这种情况中,对于第一个表的默认名字是“Table 1 ” 。如果没有在Fill方法中指定名字,每一个添加到DataSet中的表名数字都将会自增(Table2,Table3,….TableN)。
使用DataSet
DataSet将能够与ASP.NET和Windows Forms的DataGrid控件相绑定。下面是将DataSet赋值给Windows Forms中DataGrid控件的一个示例:
dgCustomers.DataSource = dsCustomers;
dgCustomers.DataMember = "Customers";
上面代码中我们做的第一件事情就是将DataSet赋值给DataGrid控件的DataSource属性。这让DataGrid知道它要绑定到那里,但是你将在GUI中得到一个’+’符号,因为DataSet能够保存多个表并且它允许你展开每一个有效的表。为了指定真正要使用哪个表,应该将DataGrid控件的DataMember属性设置为表的名字。在这个例子当中,我们设置为Customers,它与SqlDataAdapter的Fill方法中使用的第二个参数具有相同名字。这就是为什么我喜欢在Fill方法中指定表名字的原因,这样在后面的代码中就更具有可读性。
更新改变
在对数据做了修改以后,你将需要将改变写回给数据库。参考在这篇文章开始介绍的关于update的讨论,下面的代码展示了如何使用SqlDataAdapter的Update方法将改变传回数据库。
daCustomers.Update(dsCustomers, "Customers");
上面的Update方法调用了最初填充dsCustomers数据集的SalDataAdapter实例。Update方法的第二个参数指定了从数据集中的哪个表来更新。这张表中包含一组被修改的记录,并且使用SqlDataAdapter的Insert,Update和Delete属性包含的SQL语句来做数据库的修改。
整合
直到现在,你看到的代码片断需要实现断开数据管理。你真正需要的看看所有这些在一个应用程序如何实现。Listing1展示了从前面的代码断如何在程序中运作的,它已经简要的增强了本课的重点:
Listing 1: Implementing a Disconnected Data Management Strategy
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Windows.Forms;
class DisconnectedDataForm : Form
{
private SqlConnection conn;
private SqlDataAdapter daCustomers;
private DataSet dsCustomers;
private DataGrid dgCustomers;
private const string TableName = "Customers";
// initialize form with DataGrid and Button
public DisconnectedDataForm()
{
// fill dataset
InitData();
// set up datagrid
dgCustomers = new DataGrid();
dgCustomers.Location = new Point(5, 5);
dgCustomers.Size = new Size(
this.ClientRectangle.Size.Width - 10,
this.ClientRectangle.Height - 50);
dgCustomers.DataSource = dsCustomers;
dgCustomers.DataMember = TableName;
// create update button
Button btnUpdate = new Button();
btnUpdate.Text = "Update";
btnUpdate.Location = new Point(
this.ClientRectangle.Width/2 - btnUpdate.Width/2,
this.ClientRectangle.Height - (btnUpdate.Height + 10));
btnUpdate.Click += new EventHandler(btnUpdateClicked);
// make sure controls appear on form
Controls.AddRange(new Control[] { dgCustomers, btnUpdate });
}
// set up ADO.NET objects
public void InitData()
{
// instantiate the connection
conn = new SqlConnection(
"Server=(local);DataBase=Northwind;Integrated Security=SSPI");
// 1. instantiate a new DataSet
dsCustomers = new DataSet();
// 2. init SqlDataAdapter with select command and connection
daCustomers = new SqlDataAdapter(
"select CustomerID, CompanyName from Customers", conn);
// 3. fill in insert, update, and delete commands
SqlCommandBuilder cmdBldr = new SqlCommandBuilder(daCustomers);
// 4. fill the dataset
daCustomers.Fill(dsCustomers, TableName);
}
// Update button was clicked
public void btnUpdateClicked(object sender, EventArgs e)
{
// write changes back to DataBase
daCustomers.Update(dsCustomers, TableName);
}
// start the Windows Form
static void Main()
{
Application.Run(new DisconnectedDataForm());
}
}
Listing1中的InitData方法包含需要建立SqlDataAdapter和DataSet的方法。注意不同数据对象是被定义在class级别的,这样它们能够在多个方法中使用,DataGrid的DataSource属性在构造函数中设置。无论用户在何时点击更新按钮,在btnupdateClicked事件中的Update方法都被调用,将修改后的数据返回给数据库。
总结
DataSet存有多张表,能够保存在内存中并能够重用。SqlDataAdapter使你能够填充DataSet并将更新返回给数据库。你不需要担心打开和关闭数据库的连接,因为SqlDataAdapter自动完成了。一个SqlComandBuilder基于SqlDataAdapter的select语句产生insert,update和delete命令。使用SqlDataAdapter的Fill方法对DataSet填充数据集。调用SqlDataAdapter的Update方法将改变返回给数据库。