检索和修改数据
一、DataReader检索数据⚓︎
01 DataReader⚓︎
可以使用 DataReader 从数据库中检索只读、只进的数据流。 查询结果在查询执行时返回,在并存储在客户端的网络缓冲区中,直到使用 DataReader 的 Read 方法对它们发出请求。 使用 DataReader 可以提高应用程序的性能,原因是它只要数据可用就立即检索数据,并且(默认情况下)一次只在内存中存储一行,减少了系统开销。
每个数据提供程序都具有一个DbDataReader 对象 1(OleDbDataReader
、SqlDataReader
、OdbcDataReader
、OracleDataReader
)。
如何选择 DataSet 和 DataReader ?
使用 DataSet 可执行以下操作:
在应用程序中将数据缓存在本地,以便可以对数据进行处理。 如果只需要读取查询结果,则 DataReader 是更好的选择。
在层间或从 XML Web services 对数据进行远程处理。
与数据进行动态交互,例如绑定到 Windows 窗体控件或组合并关联来自多个源的数据。
对数据执行大量的处理,而不需要与数据源保持打开的连接,从而将该连接释放给其他客户端使用。
如果不需要 DataSet 所提供的功能,则可以通过使用 DataReader 以只进、只读方式返回数据,从而提高应用程序的性能。 虽然 DataAdapter 使用 DataReader 来填充 DataSet 的内容,但使用DataReader 可以提升性能,因为这样可以节省 DataSet 所使用的内存,并将省去创建 DataSet 并填充其内容所需的处理。
02 检索数据⚓︎
使用 DataReader 检索数据需要先创建 Command 对象的实例,然后调用 ExecuteReader()
方法创建一个 DataReader 实例,以便从数据源检索行。 DataReader 提供未缓冲的数据流,该数据流使过程逻辑可以有效地按顺序处理从数据源中返回的结果。
Tip
由于数据不在内存中缓存,所以在检索大量数据时,DataReader 是一种适合的选择。
下面的示例给出了使用 DataReader 从 SqlServer 检索数据的方法:
static void HasRows(SqlConnection connection)
{
using (connection)
{
string queryString = "SELECT CategoryID, CategoryName FROM Categories;";
SqlCommand command = new SqlCommand(queryString, connection);
connection.Open();
// 创建 DataReader 实例
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
Console.WriteLine("{0}\t{1}", reader.GetInt32(0),
reader.GetString(1));
}
}
else
{
Console.WriteLine("No rows found.");
}
reader.Close();
}
}
每次使用完 DataReader 对象后都应调用 Close 方法关闭它。如果 Command 包含输出参数或返回值,那么在 DataReader 关闭之前,将无法访问这些值 。
Warning
当 DataReader 打开时,该 DataReader 将以独占方式使用 Connection 。 在原始 DataReader 关闭之前,将无法对 Connection 执行任何命令(包括创建另一个 DataReader) 。
03 多结果集检索⚓︎
DataReader 可以返回多个检索的结果集合,这种场景下可以调用实例的 NextResult()
方法来按顺序循环访问这些结果集。
以下示例显示 SqlDataReader 如何使用 ExecuteReader 方法处理两个 SELECT 语句的结果:
static void RetrieveMultipleResults(SqlConnection connection)
{
using (connection)
{
string queryString1 = "SELECT CategoryID, CategoryName FROM dbo.Categories;";
string queryString2 = "SELECT EmployeeID, LastName FROM dbo.Employees";
SqlCommand command = new SqlCommand(queryString1 + queryString2, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.HasRows)
{
Console.WriteLine("\t{0}\t{1}", reader.GetName(0),
reader.GetName(1));
while (reader.Read())
{
Console.WriteLine("\t{0}\t{1}", reader.GetInt32(0),
reader.GetString(1));
}
// 访问下一个结果集
reader.NextResult();
}
}
}
二、DataAdapter操纵数据⚓︎
01 DataAdapter⚓︎
DataAdapter 用于从数据源检索数据并填充 DataSet 中的表。 DataAdapter 还可将对 DataSet 所做的更改解析回数据源。 DataAdapter 使用 Connection 对象连接到数据源,并使用 Command 对象从数据源检索数据以及将更改解析回数据源。
DataAdapter 具有重要的属性:
SelectCommand
:用于从数据源中检索数据;InsertCommand
:用于按照对 DataSet 中数据的插入到数据源;UpdateCommand
:用于按照对 DataSet 中数据的更新到数据源;DeleteCommand
:用于按照对 DataSet 中数据的删除数据源中的数据。
DataAdapter 具有重要的方法:
Fill()
:用于填充数据集,添加或刷新行以与数据源中的行匹配;Update()
:为指定的 DataTable 中每个已插入、已更新或已删除的行调用相应的 INSERT、UPDATE 或 DELETE 语句。
每个数据提供程序都具有一个 DbDataAdapter 对象2(OleDbDataAdapter
、SqlDataAdapter
、OdbcDataAdapter
、OracleDataAdapter
)。
02 填充数据集⚓︎
DataSet 表示整个数据集,其中包含表、约束和表之间的关系。 由于 DataSet 独立于数据源,因此其可以包含应用程序本地的数据,也可以包含来自多个数据源的数据。DataAdapter 则用来控制与现有数据源的交互。
DataAdapter 使用 Fill()
方法用来填充数据集,方法将需要被填充的数据集对象(DataSet或DataTable)作为参数。
下面的示例演示了 Fill()
方法的用法:
// Assumes that connection is a valid SqlConnection object.
string queryString = "SELECT CustomerID, CompanyName FROM dbo.Customers";
SqlDataAdapter adapter = new SqlDataAdapter(queryString, connection);
DataSet customers = new DataSet();
// 填充数据集
adapter.Fill(customers, "Customers");
Warning
此示例中所示的代码不显式打开和关闭 Connection 连接,Fill 方法若发现连接尚未打开,它将隐式地打开,并在完成后关闭连接。
处理单一操作时,这样可以简化代码,但如果执行多项需要打开连接的操作则需要显式地开关连接(调用 Open 和 Close 方法)以提升程序性能。
应尝试使数据源的连接打开的时间尽可能短,以便释放资;源供其他客户端应用程序使用。
03 更新数据源⚓︎
DataAdapter 的 Update()
方法可以将 DataSet 中的更改解析回数据源。该方法接受 DataSet 或 DataTable 实例为参数,这个实例包含已做更改。当调用 Update()
方法时,DataAdapter 会分析已做的更改并执行相应的命令(INSERT、UPDATE 或 DELETE)。
下列展示了更新数据源的示例:
private static void AdapterUpdate(string connectionString)
{
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlDataAdapter dataAdapter = new SqlDataAdapter(
"SELECT CategoryID, CategoryName FROM Categories",
connection);
dataAdapter.UpdateCommand = new SqlCommand(
"UPDATE Categories SET CategoryName = @CategoryName " +
"WHERE CategoryID = @CategoryID", connection);
dataAdapter.UpdateCommand.Parameters.Add(
"@CategoryName", SqlDbType.NVarChar, 15, "CategoryName");
SqlParameter parameter = dataAdapter.UpdateCommand.Parameters.Add(
"@CategoryID", SqlDbType.Int);
parameter.SourceColumn = "CategoryID";
parameter.SourceVersion = DataRowVersion.Original;
DataTable categoryTable = new DataTable();
dataAdapter.Fill(categoryTable);
DataRow categoryRow = categoryTable.Rows[0];
categoryRow["CategoryName"] = "New Beverages";
dataAdapter.Update(categoryTable);
Console.WriteLine("Rows after update.");
foreach (DataRow row in categoryTable.Rows)
{
{
Console.WriteLine("{0}: {1}", row[0], row[1]);
}
}
}
}
04 查询分页⚓︎
查询结果分页是以较小数据子集(即页)的形式返回查询结果的过程。 它通常用于以易于管理的小块形式向用户显示结果。
DataAdapter 提供通过重载 Fill()
方法仅返回一页数据的功能。 但是,对于大量的查询结果,它可能并不是首选的分页方法,因为 DataAdapter 虽然仅使用所请求的记录来填充 DataTable 或 DataSet,但仍会使用返回整个查询的资源。 若要在从数据源中返回一页数据时不使用返回整个查询的资源,请为查询指定附加条件,使返回的行数减少到只返回所需的行。
若要使用 Fill()
方法返回一页数据,请指定 startRecord 参数(代表该数据页中的第一个记录),并指定 maxRecords 参数(代表该数据页中的记录数)。
int currentIndex = 0;
int pageSize = 5;
string orderSQL = "SELECT * FROM Orders ORDER BY OrderID";
// Assumes that connection is a valid SqlConnection object.
SqlDataAdapter adapter = new SqlDataAdapter(orderSQL, connection);
DataSet dataSet = new DataSet();
adapter.Fill(dataSet, currentIndex, pageSize, "Orders");
在上例中,DataSet 只填充了 5 个记录,但却返回了整个 Orders 表 。 若要用相同的 5 个记录填充 DataSet 但仅返回这 5 个记录,请在 SQL 语句中使用 TOP 和 WHERE 子句,如以下代码示例所示。
int pageSize = 5;
string orderSQL = "SELECT TOP " + pageSize +
" * FROM Orders ORDER BY OrderID";
SqlDataAdapter adapter = new SqlDataAdapter(orderSQL, connection);
DataSet dataSet = new DataSet();
adapter.Fill(dataSet, "Orders");
请注意,当以这种方式进行查询结果分页时,必须保留用于对行进行排序的唯一标识符,以便将唯一 ID 传递给用于返回下一页记录的命令,如以下代码示例所示。
05 数据更改事件⚓︎
ADO.NET DataAdapter 公开三个可用于响应数据源中数据更改的事件。
事件 | 描述 |
---|---|
RowUpdating | 将要开始对某行执行 UPDATE、INSERT 或 DELETE 操作(通过调用 Update 方法之一) |
RowUpdated | 对某行的 UPDATE、INSERT 或 DELETE 操作(通过调用 Update 方法之一)已完成。 |
FillError | 执行 Fill 操作期间出错。 |