乐趣区

C-数据操作系列-3-ADONET-离线查询

0. 前言

在上一篇中,我故意留下了查询的示范没讲。虽然说可以通过以下代码获取一个 DataReader:

IDataReader reader = command.ExecuteReader();

然后通过 reader 一行一行的读取数据,但是我并不推荐这样使用。

在查询这一高频需求上,C# 为之做了很多工作,提供了更多的选择。这里介绍一个查询的另一套写法。

1. 离线查询

C# 在查询上提供了另一种机制,可以一次性从数据库把结果读取到网络缓存区中,直到使用的时候才加载到程序中。

在离线查询里最关键的三个接口或类:

  • IDataAdapter 一种适配器,用来获取数据并填充或更新 DataSet
  • DataSet 表示数据在内存中的缓存
  • DataTable 表示内存中一个数据表

IDataAdapter 用来提供数据,DataSet 表示 adapter 读取的结果集,其中有一个 DataTable 集合表示执行的 SQL 查询结果。至于为什么是集合,是因为 IDataAdapter 允许运行多条查询语句。

好,让我们粗略浏览一下这个三个关键点的属性和方法:

IDataAdapter:

public int Fill (System.Data.DataSet dataSet);// 将查询出来的结果填充到 DataSet 里 

在 C# 内部,其实不允许推荐直接继承该接口,推荐继承 DataAdapter 类,该类规定了数据库 Adapter 在初始化的时候,必须提供一个可以访问的数据库连接和要执行的命令文本。

当然其部分实现类允许以属性的形式后赋值这两个关键内容。

DataSet:

public DataSet ();
public DataSet (string dataSetName);// 指定数据集的名称
public System.Data.DataTableCollection Tables {get;}// 获取包含在 DataSet 中的表的集合 

DataSet 有很多有用的方法,但是在今天我们只用关系这些就可以了。

其中 Tables 引入了一个没有提到的类型,DataTableCollection。那么我们可以顺藤摸瓜,来看看里面有什么关键的内容:

public System.Data.DataTable this[int index] {get;}// 获取指定下标的 DataTable
public System.Data.DataTable this[string name] {get;}// 获取具有指定名称的 DataTable

可以看到提供了一种我们可以获取到里面的 DataTable 元素的索引访问方式。

DataTable:

public System.Data.DataSet DataSet {get;}// 获取此表所属的 DataSet。public System.Data.DataColumnCollection Columns {get;}// 获取属于该表的列的集合
public System.Data.DataRowCollection Rows {get;}// 获取属于该表的行的集合 

又出现了两个新的类:DataColumnCollection、DataRowCollection。这是一种内部集合的实现类,功能类似于 List,但又不等同于 List。

我们大概看一下对我们有用的属性和方法:

DataColumnCollection:

public virtual int Count {get;}// 获取集合中的元素总数
public System.Data.DataColumn this[int index] {get;}// 从集合中获取位于指定索引位置的 DataColumn
public System.Data.DataColumn this[string name] {get;}// 从具有指定名称的集合中获取 DataColumn。

DataRowCollection:

public override int Count {get;}
public System.Data.DataRow this[int index] {get;}// 获取索引处的行 

嗯,好先到此为止。调转方向回到上个路口,重新来。让我们看看 DataColumn 和 DataRow 又有哪些值得我们现在关注的:

DataColumn:

public string ColumnName {get; set;}// 获取或设置 DataColumnCollection 中的列的名称
public Type DataType {get; set;}// 获取或设置存储在列中的数据的类型 

DataRow:

public object this[System.Data.DataColumn column] {get; set;}// 获取或设置指定 DataColumn 中存储的数据
public object this[int columnIndex] {get; set;}// 获取或设置由索引指定的列中存储的数据
public object this[string columnName] {get; set;}// 获取或设置由名称指定的列中存储的数据
public object[] ItemArray { get; set;}// 通过数组获取或设置此行的所有值 

到目前为止,离线查询的支持类和接口就介绍了个大概。那么我们看看如何进行一个离线查询吧

2. 实践看看

以 SQL Server 数据库为例:

获取一个 SqlDataAdapter,C# 提供了四种方式获取:

public SqlDataAdapter ();// 构造一个没有连接和命令的 Adapter 对象
public SqlDataAdapter (System.Data.SqlClient.SqlCommand selectCommand);// 指定一个查询命令
public SqlDataAdapter (string selectCommandText, System.Data.SqlClient.SqlConnection selectConnection);// 指定查询命令,和连接
public SqlDataAdapter (string selectCommandText, string selectConnectionString);// 指定查询命令和连接字符串 

引用命名空间:

using System.Data;
using System.Data.SqlClient;

那么,我们先构造一个 Adapter:

var connectStr = "Data Source=.;Initial Catalog=Old;Integrated Security=True";
var sql = "select * from Area_PostCode";
var adapter = new SqlDataAdapter(sql, connectStr);

然后创建一个用于保存数据的 DataSet,并把数据填充进去:

DataSet set = new DataSet();
adapter.Fill(set);

然后可以看到这个 set 中的数据应该是这样的:

上图是在 VS 中的调试模式中,可以看到

根据上图我们大概可以猜测一下 DataTable 内部的数据结构,或者 C# 让我们理解的结构是什么。

其中 DataColumn 对应着图中列,ColumnName 就是图 所示的列名。而 DataRow 就是行,ItemArray 则是一行行数据。

这样一来,显然就比直接使用 IDataReader 访问数据要方便很多。

依据上例:

我们试着获取一下第三行的 Province 列值,如果觉得这个表述别扭的话,看一下我的写法,就知道我为什么这么表示了。

var table = set.Tables[0];// 先拿到第一个表
var value = table.Rows[2]["Province"];

这是一种蚂蚁搬家式的读取数据方式。C# 为 DataTable 提供了一个扩展方法:

public static EnumerableRowCollection<DataRow> AsEnumerable(this DataTable source);

将表格转换成可枚举的 DataRow 集合。

所以我们可以用 foreach 循环来遍历 DataTable。

3. 未完待续

在这一节简单介绍了一下 ADO.NET 的离线查询支持。当我们能从数据库中获取到 DataTable 的时候,我们就能通过这个做出更多的事情来。下一章我将带领大家结合之前介绍的反射,实现一个简单的 ORM 工具类。

更多内容烦请关注我的博客《高先生小屋》

退出移动版