ADO.NET DataSet 是数据的内存驻留表示形式,它提供了独立于数据源的一致关系编程模型。DataSet 表示整个数据集,其中包含表、约束和表之间的关系。由于 DataSet 独立于数据源,DataSet 可以包含应用程序本地的数据,也可以包含来自多个数据源的数据。与现有数据源的交互通过 DataAdapter 来控制。

随 .NET 框架提供的每个 .NET 数据提供程序都包含一个 DataAdapter 对象:OLE DB .NET 数据提供程序包含一个 OleDbDataAdapter 对象,而 SQL Server .NET 数据提供程序包含一个 SqlDataAdapter 对象。DataAdapter 对象用于从数据源中检索数据并填充 DataSet 中的表。DataAdapter 还会将对 DataSet 作出的更改解析回数据源。DataAdapter 使用 .NET 数据提供程序的 Connection 来连接到数据源,使用 Command 对象从数据源中检索数据并将更改解析回数据源。

DataAdapterSelectCommand 属性是一个 Command 对象,它从数据源中检索数据。DataAdapterInsertCommandUpdateCommandDeleteCommand 属性也是 Command 对象,它们按照对 DataSet 中数据的修改来管理对数据源中数据的更新。这些属性将在使用 DataAdapter 和 DataSet 更新数据库中详细介绍。

DataAdapterFill 方法用于使用 DataAdapterSelectCommand 的结果来填充 DataSetFill 将要填充的 DataSetDataTable 对象(或要使用从 SelectCommand 中返回的行来填充的 DataTable 的名称)用作它的参数。

Fill 方法使用 DataReader 对象来隐式地返回用于在 DataSet 中创建表的列名称和类型以及用来填充 DataSet 中的表行的数据。表和列仅在不存在时才创建;否则,Fill 将使用现有的 DataSet 架构。列类型按照将 .NET 数据提供程序数据类型映射到 .NET 框架数据类型中的表创建为 .NET 框架类型。除非数据源中存在主键并且 DataAdapter.MissingSchemaAction 设置为 MissingSchemaAction.AddWithKey,否则不会创建主键。如果 Fill 发现存在用于某表的主键,那么对于其中的主键列值与从数据源中返回的行的主键值相匹配的行,它将使用数据源中的数据改写 DataSet 中的数据。如果未找到任何主键,则数据将追加到 DataSet 中的表。当填充 DataSet 时,Fill 会使用任何可能存在的 TableMappings(请参阅设置 DataTable 和 DataColumn 映射)。

以下代码示例创建 DataAdapter 的一个实例,该实例使用与 Microsoft SQL Server Northwind 数据库的 Connection 并使用客户列表来填充 DataSet 中的 DataTable。向 DataAdapter 构造函数传递的SQL 语句和 Connection 参数用于创建 DataAdapterSelectCommand 属性。

SqlClient

Dim nwindConn As SqlConnection = New SqlConnection("Data 
Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind")

Dim selectCMD As SqlCommand = New SqlCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn)
selectCMD.CommandTimeout = 30

Dim custDA As SqlDataAdapter = New SqlDataAdapter
custDA.SelectCommand = selectCMD

nwindConn.Open()

Dim custDS As DataSet = New DataSet
custDA.Fill(custDS, "Customers")

nwindConn.Close()

SqlConnection nwindConn = new SqlConnection("Data Source=
localhost;Integrated Security=SSPI;Initial Catalog=northwind");

SqlCommand selectCMD = new SqlCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn);
selectCMD.CommandTimeout = 30;

SqlDataAdapter custDA = new SqlDataAdapter();
custDA.SelectCommand = selectCMD;

nwindConn.Open();

DataSet custDS = new DataSet();
custDA.Fill(custDS, "Customers");

nwindConn.Close();

OleDb

Dim nwindConn As OleDbConnection = New OleDbConnection
("Provider=SQLOLEDB;Data Source=localhost;"Integrated Security=SSPI;Initial Catalog=northwind")

Dim selectCMD As OleDbCommand = New OleDbCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn)
selectCMD.CommandTimeout = 30

Dim custDA As OleDbDataAdapter = New OleDbDataAdapter
custDA.SelectCommand = selectCMD

Dim custDS As DataSet = New DataSet
custDA.Fill(custDS, "Customers")

OleDbConnection nwindConn = new OleDbConnection("Provider=
SQLOLEDB;Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind");

OleDbCommand selectCMD = new OleDbCommand("SELECT CustomerID, CompanyName FROM Customers", nwindConn);
selectCMD.CommandTimeout = 30;

OleDbDataAdapter custDA = new OleDbDataAdapter();
custDA.SelectCommand = selectCMD;

DataSet custDS = new DataSet();
custDA.Fill(custDS, "Customers");

请注意,该代码不显式打开和关闭 Connection。如果 Fill 方法发现连接尚未打开,它将隐式地打开 DataAdapter 正在使用的 Connection。如果 Fill 已打开连接,它还将在 Fill 完成时关闭连接。当处理单一操作(如 FillUpdate)时,这可以简化您的代码。但是,如果您在执行多项需要打开连接的操作,则可以通过以下方式提高应用程序的性能:显式调用 ConnectionOpen 方法,对数据源执行操作,然后调用 ConnectionClose 方法。为了释放资源供其他客户端应用程序使用,应设法使与数据源的连接打开尽可能短的时间。

多个结果集

如果 DataAdapter 遇到多个结果集,它将在 DataSet 中创建多个表。将向这些表提供递增的默认名称 TableN,以表示 Table0 的“Table”为第一个表名。如果以参数形式向 Fill 方法传递表名称,则将向这些表提供递增的默认名称 TableNameN,这些表名称以表示 TableName0 的“TableName”为起始。

从多个 DataAdapter 填充 DataSet

可以将任意数量的 DataAdapter 与一个 DataSet 一起使用。每个 DataAdapter 都可用于填充一个或多个 DataTable 对象并将更新解析回相关数据源。DataRelationConstraint 对象可以在本地添加到 DataSet,这样,您就可以使来自多个不同数据源的数据相关联。例如,DataSet 可以包含来自 Microsoft SQL Server 数据库、通过 OLE DB 公开的 IBM DB2 数据库以及对 XML 进行流处理的数据源的数据。一个或多个 DataAdapter 对象可以处理与每个数据源的通讯。

以下代码示例从 Microsoft SQL Server 2000 上的 Northwind 数据库填充客户列表,从存储在 Microsoft® Access 2000 中的 Northwind 数据库填充订单列表。已填充的表通过 DataRelation 相关联,然后客户列表将与相应客户的订单一起显示出来。有关 DataRelation 对象的更多信息,请参阅添加表间关系导航表间关系

Dim custConn As SqlConnection= New SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind;")
Dim custDA As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers", custConn)

Dim orderConn As OleDbConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\Program Files\Microsoft Office\Office\Samples\northwind.mdb;")
Dim orderDA As OleDbDataAdapter = New OleDbDataAdapter("SELECT * FROM Orders", orderConn)

custConn.Open()
orderConn.Open()

Dim custDS As DataSet = New DataSet()

custDA.Fill(custDS, "Customers")
orderDA.Fill(custDS, "Orders")

custConn.Close()
orderConn.Close()

Dim custOrderRel As DataRelation = custDS.Relations.Add("CustOrders", custDS.Tables("Customers").Columns("CustomerID"),custDS.Tables("Orders").Columns("CustomerID"))

Dim pRow, cRow As DataRow

For Each pRow In custDS.Tables("Customers").Rows
  Console.WriteLine(pRow("CustomerID").ToString())

  For Each cRow In pRow.GetChildRows(custOrderRel)
    Console.WriteLine(vbTab & cRow("OrderID").ToString())
  Next
Next

SqlConnection custConn = new SqlConnection("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind;");
SqlDataAdapter custDA = new SqlDataAdapter("SELECT * FROM Customers", custConn);

OleDbConnection orderConn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\\Program Files\\Microsoft Office\\Office\\Samples\\northwind.mdb;");
OleDbDataAdapter orderDA = new OleDbDataAdapter("SELECT * FROM Orders", orderConn);

custConn.Open();
orderConn.Open();

DataSet custDS = new DataSet();

custDA.Fill(custDS, "Customers");
orderDA.Fill(custDS, "Orders");

custConn.Close();
orderConn.Close();

DataRelation custOrderRel = custDS.Relations.Add("CustOrders",custDS.Tables["Customers"].Columns["CustomerID"],custDS.Tables["Orders"].Columns["CustomerID"]);

foreach (DataRow pRow in custDS.Tables["Customers"].Rows)
{
  Console.WriteLine(pRow["CustomerID"]);
   foreach (DataRow cRow in pRow.GetChildRows(custOrderRel))
    Console.WriteLine("\t" + cRow["OrderID"]);
}

SQL Server Decimal 类型

DataSet 使用 .NET 框架数据类型来存储数据。对于大多数应用程序,这些类型都提供了一种方便的数据源信息表示形式。但是,当数据源中的数据类型是 SQL Server decimal 时,这种表示形式可能会导致问题。.NET 框架 decimal 数据类型最多允许 28 个有效位,而 SQL Server decimal 数据类型则允许 38 个有效位。如果 SqlDataAdapterFill 操作过程中确定 SQL Server decimal 字段的精度大于 28 个字符,则当前行将不会被添加到 DataTable 中。此时将发生 FillError 事件,它使您能够确定是否将发生精度损失并作出适当的响应。有关 FillError 事件的更多信息,请参阅使用 DataAdapter 事件。若要获取 SQL Server decimal 值,还可以使用 SqlDataReader 对象并调用 GetSqlDecimal 方法。

OLE DB 章节

分层行集或章节(OLE DB 类型 DBTYPE_HCHAPTER、ADO 类型 adChapter)可用于填充 DataSet 的内容。当 DataAdapterFill 操作过程中遇到章节列时,将为章节列创建一个 DataTable,并使用章节中的列和行填充该表。为章节列创建的表使用父表名称和章节列名称来命名,其形式为“ParentTableNameChapteredColumnName”。如果 DataSet 中已存在与章节列的名称相匹配的表,则将使用章节数据填充当前表。如果现有表中不存在与章节中的列相匹配的列,则将添加新列。

在使用章节列中的数据填充 DataSet 中的表之前,将创建分层行集的父表和子表之间的关系,方法是向父表和子表添加一个整数列,将父列设置为自动递增,然后使用两个表中所添加的列来创建 DataRelation。所添加的关系使用父列名和子列名来命名,其形式为“ParentColumnName_ChildColumnName”。

请注意,相关列仅存在于 DataSet 中。如果在随后从数据源进行填充,则将使新行被添加到表中,而不是使更改被并入现有行。

另请注意,如果使用采用 DataTableDataAdapter.Fill 重载,则将只填充该表。仍会将自动递增整数列添加到该表中,但不会创建或填充任何子表并且不会创建任何关系。

以下示例使用 MSDataShape 提供程序为客户列表中的每个客户生成订单的章节列。然后使用相应数据来填充 DataSet

Dim nwindConn As OleDbConnection = New OleDbConnection("Provider=MSDataShape;Data Provider=SQLOLEDB;Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind")

Dim custDA As OleDbDataAdapter = New OleDbDataAdapter("SHAPE {SELECT CustomerID, CompanyName FROM Customers} APPEND ({SELECT CustomerID, OrderID FROM Orders} AS Orders RELATE CustomerID TO CustomerID)", nwindConn)

Dim custDS As DataSet = New DataSet()

custDA.Fill(custDS, "Customers")

OleDbConnection nwindConn = new OleDbConnection("Provider=MSDataShape;Data Provider=SQLOLEDB;Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind");

OleDbDataAdapter custDA = new OleDbDataAdapter("SHAPE {SELECT CustomerID, CompanyName FROM Customers} APPEND ({SELECT CustomerID, OrderID FROM Orders} AS Orders RELATE CustomerID TO CustomerID)", nwindConn);

DataSet custDS = new DataSet();
custDA.Fill(custDS, "Customers");

Fill 操作完成时,DataSet 将包含两个表:CustomersCustomersOrders,其中 CustomersOrders 表示章节列。一个名为 Orders 的附加列被添加到 Customers 表中,一个名为 CustomersOrders 的附加列被添加到 CustomersOrders 表中。Customers 表中的 Orders 列设置为自动递增。DataRelation Orders_CustomersOrders 使用已添加到以 Customers 为父表的表中的列来创建。下表显示了一些示例结果。

表名称:Customers

CustomerID CompanyName Orders
ALFKI Alfreds Futterkiste 0
ANATR Ana Trujillo Emparedados y helados 1

表名称:CustomerOrders

CustomerID OrderID CustomersOrders
ALFKI 10643 0
ALFKI 10692 0
ANATR 10308 1
ANATR 10625 1