ADO.NET的记忆碎片(三)

来源:岁月联盟 编辑:exp 时间:2011-12-02

 

接着ADO.NET的记忆碎片(二)继续

 

DataAdapter类

主要是在数据源以及DataSet 之间执行数据传输的工作,它可以透过Command 对象下达命令后,并将取得的数据放入DataSet 对象中。这个对象是架构在Command对象上,并提供了许多配合DataSet 使用的功能。

构造一个DataAdapter对象有三方法: www.2cto.com

 

string strConn ="...";//连接字符串

string strSql = "select * from MytableName1";

SqlConnection cn = new SqlConnection(strConn);

SqlCommand cmd = new SqlCommand(strSql,cn);

1、SqlDataAdapter da1 = new SqlDataAdapter(strSql,strConn);

2、SqlDataAdapter da2 = new SqlDataAdapter(strSql,cn);

3、SqlDataAdapter da3 = new SqlDataAdapter(cmd);

 

其实这三种方法很多时候可以互换使用,不过第一种有一些美中不足,当需要多次实例化的时候,并且这个strConn链接字符串也是一样的时候,会带来一个小问题,假设DataAdapter实例化了N次,那么也会实例化N个不同Connection的链接,其实我们只要一个这样的Connection的链接就好了,明显这是没有必要的性能损耗,2、3这两种方法可以显示的指定Connection实例,可以避免这样的问题。

看看构造好了的SqlDataAdapter怎样将查询的结果存储在DataSet实例中呢,可是使用Fill()方法:

 

DataSet ds = new DataSet();

da.Fill(ds);

foreach(DataRow row in ds.Tables[0].Rows)

{

    Console.WriteLine("{0}--{1}",row[0],row["CustomerName"]);

}

 

关于Fill()方法的一个小说明:其实当程序进入Fill()方法后,会先检查Connection是否打开,要是打开,就可以继续数据的处理,最后推出Fill()方法;要是没有打开,就会先打开Connection链接,然后处理数据,在退出方法之前,关闭Connection链接,在Fill()方法的前后Connection状态是没有改变的,Fill()的聚合性很强的,用起来很简单。看看下面的代码:

 

cn.Close();

DataSet ds1 = new DataSet();

DataSet ds2 = new DataSet();

da.Fill(ds1);

da.Fill(ds2);

 

可以看到Connection是关闭着的,不过Fill()还是很好的完成了数据的处理,但是我们知道,其实上面的代码性能是可以进一步提升的,因为在调用Fill()方法两次中,对Connection实例进行了:Open();Close();Open();Close();的两次调用,可以把代码改进:

 

cn.Close();

DataSet ds1 = new DataSet();

DataSet ds2 = new DataSet();

cn.Open();

da.Fill(ds1);

da.Fill(ds2);

cn.Close();

 

 我们看看存储在DataSet中的DataTable的名称是什么:

 

Console.WriteLine("{0}",ds.Tables[0].TableName);

 

 会看到是“Table”。这个结果并不能让我们兴奋,为什么是这个名称?因为,SqlDataAdapter在背后是隐式创建了一个SqlDataReader,以获得查询的结果。在SqlDataAdapter查第一行数据之前,它会收集SqlDataReader的架构的信息,已确定列名称和类型,但是在默认的情况下,查询引用的表名称不能通过SqlDataReader来获得。所以在SqlDataReader中就会赋予了“Table”作为查询结果的表名称,最后导致了上面的这个结果。我们可以使用TableMappings这个属性改变这个纠结的现象:

 

DataSet ds = new DataSet();

da.TableMappings.Add("Table","MyTableName");

da.Fill(ds);

Console.WriteLine("{0}",ds.Tables[0].TableName);//会输出MyTableName

 

 Add()方法中两个参数,第一个参数是表示数据库中的表名称,第二个参数是表示DataSet中的表名称。因为SqlDataReader不能获得真的表名,所以赋予了“Table”作为查询结果的表名称,为了性能更好,这也是没有办法的事情。

Fill()方法的重载:

 

DataSet ds = new DataSet();

DataTable table = new DataTable();

1、da.Fill(ds);

2、da.Fill(ds,"MyTableName");//表示存储在DataSet中的表名称是MyTableName,是da.TableMappings.Add("Table","MyTableName");da.Fill(ds);的语法唐

3、da.Fill(table);

 

 希望将查询的结果映到自己设置的DataSet里,使用TableMappings和ColumnMappings的设置完成自己想要的:

 

DataTableMapping tableMap;

tableMap=da.TableMappings.Add("Table","MyTableName");

tableMap.ColumnMappings.Add("EmpID","MyEmpID");

tableMap.ColumnMappings.Add("EmpName","MyEmpName");

 

 Add()方法中两个参数,第一个参数是表示数据库中的表名称,第二个参数是表示DataSet中的表名称。SqlDataReader可以获得列名称和类型,不过不能获得表名称。然后只要执行Fill()方法,就可以在DataSet中看到查询的结果并且是自己设置的表名和列名:

 

da.Fill(ds);

Console.WriteLine("{0}",ds.Tables[0].TableName);

foreach(DataColumn col in ds.Tables[0].Columns)

{

    Console.WriteLine("{0}",col.Name);

}

 

使用批量查询

有时候我们在一次查询中返回多个表结果:

 

string strConn ="...";//连接字符串

string strSql = "select * from MytableName1;"+"select * from MytableName2";

SqlDataAdapter da = new SqlDataAdapter(strSql,strConn);

da.TableMappings.Add("Table","MyTableName1");

da.TableMappings.Add("Table1","MyTableName2");

DataSet ds = new DataSet();

da.Fill(ds);

foreach(DataTable table in ds.Tables)

{

    Console.WriteLine("{0}",table.TableName);

}

 

从中可以看出来MyTableName1、MyTableName1是我们为结果中的两个表命名,而Table和Table1是SqlDataReader不能获得数据库的表名,自己给添加的。最后说一句,DataAdapter背后是隐式的创建了DataReader来获取结果集的。

 

 

摘自 八神吻你