探讨 ADO.NET DataRow(转载)

转载自:http://www.microsoft.com/china/MSDN/library/data/dataAccess/misdatapoints.mspx?mfr=true

探讨 ADO.NET DataRow

发布日期: 12/13/2004 | 更新日期: 12/13/2004

Paul DiLascia

下载本文的代码: DataPoints0310.exe (136KB)

ADO.NET 提供了可允许开发人员通过 DataTable、DataRowCollection 和 DataRow 对象与行进行交互的丰富的编程接口。因为我收到了许多有关 ADO.NET 中 DataRow 对象的问题,所以我总结了其中几个,并将在本月的“数据点”部分中提供答案。我将探讨如何使用 DataRow 来操作数据,并重点介绍如何在 ADO.NET 和 C# 中导入、加载、遍历、查找和检查数据。

*
本页内容
创建和导入行 创建和导入行
查找行 查找行
小结 小结

创建和导入行

DataTable 公开几种可用于操作 DataRow 对象的方法。它们包括 ImportRow、NewRow、Select 和 LoadDataRow。我将在示例 Windows?窗体应用程序中使用这些方法,以便向您展示如何从一个 DataSet 中抽取一行或多行,并将它们添加到第二个 DataSet 中。我还将向您展示了如何手动创建和加载 DataSet。

首先,我直接从 SQL Server?Northwind 数据库的 Employee 表加载 DataSet(请参阅图 1),并将它绑定到 DataGrid(请参阅图 2)。在加载了 DataSet 之后,我在 EmployeeID 列上为 DataTable 创建了主键约束。由于该列是 Northwind 数据库中的 IDENTITY 列,因此,我还将 AutoIncrement 属性设置为 true,并将 AutoIncrementSeed 和 AutoIncrementStep 属性都设置为 –1。这会通知 DataTable:在向该 DataTable 中添加行时,EmployeeID DataColumn 的值应当由 DataTable 生成,该值从 -1 开始并逐行减 1。因此,在所添加的任何行中,EmployeeID 的值将为 –1、-2、-3 等。这非常重要,因为我无法知道在发送要插入的新行时,数据库将赋予什么样的 EmployeeID 值。因此,我赋予了一个已知不能由数据库赋予的值(负数)。于是,当我向数据库发送要插入的数据时,将忽略 EmployeeID 值,并将从数据库中检索新创建和赋予的值。(有关详细信息,请参考 2003 年 7 月份的 MSDN? Magazine¡°Managing Hierarchical Inserts in ASP.NET and ADO.NET”中的“数据点”专栏。)

接下来,我使用代码手动创建了第二个 DataSet,然后向其中添加了两行 employees(请参阅图 3)。在该代码示例中,我创建了一个 DataTable,并向其中添加了多个 DataColumn 对象,这些对象与第一个 DataSet (m_oDs1) 中的 DataColumn 对象相匹配。我还向该表中添加了主键约束,以便使这两个 DataTable 和 DataSet 保持同步。然而,请注意,我将 AutoIncrementSeed 设置为 –1000(请参阅图 3)。这样,当我从这个手动创建的 DataTable 中提取行并将它们复制到从数据库中派生的 DataTable 时,任何一行都没有重复的 EmployeeID。对于避免出现非唯一的键值,这是一个简单而有效的解决方案。

接着,我使用 NewRow 方法为手动创建的 DataTable 创建新行(同样,请参阅图 3)。这看上去可能有些笨拙,因为此处真正发生的情况是,要向 DataTable 中添加新行,我实际上必须向 DataTable 请求新行,设置它的值,然后向 DataTable 添加新行。因此,当我调用 NewRow 方法时,新创建的行实际上将从 DataTable 分离出来。要附加它,随后必须将它传递到 DataTable 的 Rows 集合的 Add 方法。

执行以下代码,可以将第二个 DataSet(手动创建的 DataSet)中的行导入到第一个 DataSet(来自 Northwind 数据库)中。

//-- Import the rows
foreach (DataRow oRow in m_oDs2.Tables["Employees"].Rows)
{
m_oDs1.Tables["Employees"].ImportRow(oRow);
}

该代码循环通过我添加到手动创建的 DataTable 中的两行,并通过 ImportRow 方法将它们依次导入到从 Northwind Employee 表中派生的 DataSet 中。

要导入数据,您只需执行上述操作。但是,要记住所导入数据的一些重要方面。在将数据从 DataTable 导入到另一个 DataTable 中时,还将导入 RowState。为了说明这一点,我向 Windows 窗体额外添加了两个 DataGrid 对象(请参阅图 1)。我向第一个 DataSet 添加了第二个 DataTable 以存储每一行的 RowState,第一个 DataSet 已经组合了 Northwind 中 Employees 的 DataTable。因此,在第一个 DataSet (m_oDs) 中有两个 DataTable 对象,一个对象引用 employees,另一个对象与第一个对象具有相同数量的行。使用第二个 DataTable(我称之为 State)的目的仅在于存储 Employee DataTable 中每一行的 RowState。

第二个 DataSet 已经包含了一个 DataTable,用来存储我用代码手动创建的雇员。我又向其中添加了一个 DataTable,以便存储其中每一行的 RowState。此处的目的在于说明 ImportRow 方法如何维护行的 RowState。对于从数据库的 Employees 表检索的行以及存储在第一个 DataSet m_oDs1 中的行来说,它们的 RowState 均设置为“unchanged”,因为我未对它们执行任何操作。我手动添加到第二个 DataSet m_oDs2 中的行的 RowState 值均为“added”,这是由于我亲自添加了它们。因此,当我将这两行导入到 m_oDs1 DataSet 中时,这两个新行的 RowState 仍为“added”。

顺便说一句,如果我在添加了这两行之后针对第一个 DataSet 调用了 AcceptChanges 方法,它们的 RowState 值将设置为“unchanged”。因此,当我在后来将它们导入到 m_oDs2 DataSet 中时,它们的 RowState 也将为“unchanged”。在针对 DataTable 调用时,AcceptChanges 方法通知 DataTable:每个 DataRow 的初始值将设置为最新的值,并针对每一行生成状态“unchanged”RowState。该示例的完整代码还包括用来跟踪 DataGrid RowState 的代码。

在讨论本主题时,需要注意另一种方法 — LoadDataRow。实质上,ImportRow 方法将一个 DataRow 从一个 DataTable 复制到另一个 DataTable,并假设这两个 DataTable 对象具有相同的列。LoadDataRow 方法还可用于复制行。以下代码显示了如何在以上代码示例中使用 ImportRow,将 DataRow 从一个 DataTable 复制到另一个 DataTable:

//-- Import Row
m_oDs1.Tables["Employees"].ImportRow(oRow);
这也可以通过使用 LoadDataRow 方法来完成,如下所示:
//-- Load Data Row
object[] aRowValues = {oRow["EmployeeID"], oRow["FirstName"],
oRow["LastName"]};
m_oDs1.Tables["Employees"].LoadDataRow(aRowValues, false);

由于行只是被复制,因此,当两个 DataTable 对象具有相同的列时,ImportRow 更实用。但是,LoadDataRow 更加灵活一些。例如,如果 DataTable 对象没有同样的列,就可以使用 LoadDataRow,而不能使用 ImportRow。这是由于 LoadDataRow 方法接受要插入的值的数组(在本例中,将插入到 DataTable 中)。因此,如果在这两个 DataTable 对象中存在不同的列,那么,LoadDataRow 只需按正确的顺序从第二个 DataTable 中获取要插入到第一个 DataTable 中的相应值。

但是,除了将行插入到 DataTable 中以外,LoadDataRow 方法还有另外一个目的。实际上,当有必要更新现有的行(如果已经存在一行的话)或者添加行(如果尚且不存在行的话)时,LoadDataRow 最有用。如果找到了行的键值,LoadDataRow 将更新该行的值,而不是插入新行。这只是显示 LoadDataRow 比 ImportRow 更灵活的另一种方法,因为在这种情况下,ImportRow 会在某一行已经存在重复键值时引发异常。

查找行


下面的代码示例解决了我收到的有关如何查找行的几个问题。使用 ADO.NET 可以有多种方法来查找行,这里我将演示三个最典型方法的示例:Find、Contains 和 Select 方法。所有这三种方法都可用于查找特定行;但是,它们是以截然不同的方式来完成该任务的。

Find 方法按照行的主键值查找行。如果找到了某一行,则返回该行的主键值;如果未找到行,将返回空值。当您需要查找某一行并针对它随后执行操作时,Find 方法非常有用。当我将雇员的 DataGrid 与它的 RowState 同步时,我使用的是本专栏中第一个示例中的 Find 方法。在该示例中,这两个 DataGrid 对象具有相同的值 — EmployeeID。我使用这两个 DataGrid 对象的 EmployeeID DataColumn,在它们的基础 DataTable 对象上创建了主键约束。这样,我就能够循环访问 Employees DataTable 中的行,并将相应的 State DataTable 中的行设置为适当的值。请注意以下代码示例如何循环访问 Employees DataTable 中的每个雇员,并使用 Find 方法从 State DataTable 检索与之匹配的行。

DataRow oStateRow;
foreach (DataRow oRow in m_oDs1.Tables["Employees"].Rows)
{
oStateRow = m_oDs1.Tables["State"].Rows.Find(oRow["EmployeeID"]);
oStateRow["RowState"] = oRow.RowState;
}

在本例中,为了使 Find 方法正确工作,必须有主键。EmployeeID DataColumn 必须有主键约束;否则,Find 方法将引发异常。Find 方法依赖约束来查找所需的行。由于 Find 方法需要主键约束,因此无需担心会找到多行。如果 DataTable 有多个 DataColumn 对象,这些对象构成导致复合键的主键,我可能只是将表示键值的值数组传递给 Find 方法。

如果我不需要针对行进行查找和执行操作,而只需知道 DataTable 中是否存在与主键值相匹配的行,那么,我可以使用 Contains 方法。当您需要查看行是否存在,但不需要检索它时,Contains 方法非常有用。Contains 方法的工作方式类似于 Find 方法,这是由于可以向 Contains 方法传递单个值或值数组(具体情况取决于构成其主键的列的数量)。正如 Find 一样,Contains 依赖于主键的存在与否,因此,在选择该方法之前一定要确保存在一个主键。Contains 提供了一种简单的方法来查看行是否存在,而 Find 不仅查看是否存在行,而且还返回行。

第三种方法是使用 DataTable 对象的 Select 方法。它返回符合指定条件的 DataRow 对象的数组。Select 方法有许多重载窗体,因此,可以通过使用字符串表达式来筛选它并以特定的排序顺序返回,也可以对其进行筛选,以便只显示某些行状态。

Select 和 Find 方法有一个重要区别,那就是 Find 搜索与所传入的主键值相匹配的一行。这个 Find 方法可以返回零行或一行,而 Select 方法可以返回零行或多行。在这方面,Select 更加灵活,因为它与是否存在主键约束无关。它搜索所有符合条件的行,并以 DataRow 对象的数组形式返回它们。为了比较 Find 和 Select 方法,在下面的代码片断中,我显示了使用以上示例应用程序中的 Find 的代码,以及在使用 Select 时替换代码的外观:

//-- Using the Find method
oStateRow = m_oDs1.Tables["State"].Rows.Find(oRow["EmployeeID"]);
oStateRow["RowState"] = oRow.RowState;
//-- Using the Select method
DataRow[] aRow = m_oDs1.Tables["State"].Select("EmployeeID = " +
oRow["EmployeeID"]);
aRow[0]["RowState"] = oRow.RowState;

在本例中,因为我总是查找单行,所以 Find 方法更高效。可以使用 Select,但是,不如只使用主键值好。另请注意,在使用 Select 方法的示例代码中,我如何将行索引硬编码为第一行(索引为 0)。我可能已经循环访问了返回的行,但是,由于我知道将只返回一行,我冒险对它对进行了硬编码。很显然,在查找单行时,这不是最好的方法。

不过,在搜索未知数量的行时,Select 方法可能会非常有效。例如,如果我希望查找所有来自 Seattle 的雇员(假设 City 列位于我的 DataTable 中),那么,可以通过使用以下代码来方便地执行查找:

DataRow[] aRow = oDs.Tables["Employees"].Select("City = 'Seattle'");还可以通过按如下方式指定 Select 方法的排序顺序参数,对数组中检索到的 DataRow 对象进行排序:

DataRow[] aRow = m_oDs2.Tables["Employees"].Select("City = 'Seattle'",
"LastName");

这将返回两个居住在 Seattle 的雇员,并按他们的姓进行排序。Select 方法还可以使用 DataViewRowState 对行进行筛选。以下代码将返回刚添加的、来自 Seattle 的雇员,并按他们的姓进行排序:

DataRow[] aRow = m_oDs2.Tables["Employees"].Select("City = 'Seattle'",
"LastName", DataViewRowState.Added);

小结


本专栏说明了如何检索行,查看某一行是否存在,基于键值查找特定的行,手动创建表,创建新行并在 DataTable 对象之间复制行。它还简略提到 DataRowState,以及在使用 ImportRow 方法导入行时如何维护它。在 ADO.NET 中,DataRowState 和 DataViewRowState 在许多方面都非常有用,在处理并发时尤其如此,我将在以后的“数据点”专栏中讨论。


将您对 John 的问题和评论发送到 mmdata@microsoft.com

John Papa 是个棒球狂热的爱好者,他与两个小女儿、妻子和忠实的狗 Kadi 通常一起在 Yankees 避暑。他撰写过几本有关 ADO、XML 和 SQL Server 方面的书籍,并屡屡在行业会议(如 VSLive)上发言。您可以通过 mmdata@microsoft.com 与他联系。

原文地址:https://www.cnblogs.com/Koy/p/534844.html