ado.net中的多数据表操作浅析—读取
作者:郑佐??????? 2004-8-5
???????? 在开发基于.net平台的数据库应用程序时,我们一般都会用到dataset,作为ado.net的核心类它为我们提供了强大的功能,而整个看上去就像是放在内存内的一个小型数据库,内部包括了datatable、dataview、datarow、datacolumn、constraint以及datarelation。当初看到它时真的是有点兴奋。
???????? 下面根据我的一些经验来举例说明在ado.net中的多表填充、关联表更新以及多个command对象执行过程中启用事务的操作。欢迎大家交流,或在blog上留言。
????????
一、准备工作
???????? 对于northwind数据库大家都比较熟悉,所以这里拿它为例,我把customers(客户表)、orders(订单表)、order details(订单详细表)合起来建立了一个类型化的数据集,类型名称为datasetorders,每个表只包括一些字段,下面是在visual studio .net中建立的一个截图:
?
图1-1
上面建立了两个关系表示为customers —> orders —>order details。因为orders表的orderid字段为自动增长列,这里把就把它的autoincrementseed和autoincrementstep值设置成了-1,这在实际添加订单的过程中可能会比较明显,不过不设也没问题。
????????
二.填充数据集
建立一个窗体程序来演示实际的操作,界面如下:
图2-1
整个应用程序就是一个form,上面的三个datagrid分别用来显示相关表的数据,不过他们是互动的。另外的两个单选框用来决定更新数据的方式,两个按钮正如他们的名称来完成相应的功能。
这里我们用一个dataadapter来完成数据集的填充,执行的存储过程如下:
create procedure getcustomerordersinfo
as
select customerid,companyname,contactname from customers where customerid like a%
?
select orderid,orderdate,customerid from orders? where customerid in
(select customerid from customers where customerid like a%)
?
select orderid,productid,unitprice,quantity,discount from [order details] where orderid in
(select orderid from orders? where customerid in
(select customerid from customers where customerid like a%))
?
go
?
为了减少数据量,这里只取了customerid以’a’开头的数据。
建立dataaccess类来管理窗体同数据层的交互:
using system;
using system.data;
using system.data.sqlclient;
using microsoft.applicationblocks.data;
?
namespace winformtest
{
???? public class dataaccess
???? {
???????? private string _connstring = "data source=(local);initial catalog=northwind;uid=csharp;pwd=c#.net2004;";
???????? private sqlconnection _conn;
???????? ///构造函数
public dataaccess()
???????? {
????????????? _conn = new sqlconnection(_connstring);
}
下面的函数完成单个数据适配器来完成数据集的填充,
public void fillcustomerordersinfo(datasetorders ds)
???? ???? {
????????????? sqlcommand comm = new sqlcommand("getcustomerordersinfo",_conn);
????????????? comm.commandtype = commandtype.storedprocedure;
????????????? sqldataadapter dataadapter = new sqldataadapter(comm);
????????????? dataadapter.tablemappings.add("table","customers");
????????????? dataadapter.tablemappings.add("table1","orders");
????????????? dataadapter.tablemappings.add("table2","order details");
????????????? dataadapter.fill(ds);
???????? }
如果使用sqlhelper来填充那就更简单了,
???????? public void fillcustomerordersinfowithsqlhelper(datasetorders ds)
???????? {???????????? sqlhelper.filldataset(_connstring,commandtype.storedprocedure,"getcustomerordersinfo",ds,new string[]{"customers","orders","order details"});
???????? }
叉开话题提一下,data access application block 2.0中的sqlhelper.filldataset这个方法超过两个表的填充时会出现错误,其实里面的逻辑是错的,只不过两个表的时候刚好凑巧,下面是从里面截的代码:
private static void filldataset(sqlconnection connection, sqltransaction transaction, commandtype commandtype,
????????????? string commandtext, dataset dataset, string[] tablenames,
????????????? params sqlparameter[] commandparameters)
???????? {
????????????? if( connection == null ) throw new argumentnullexception( "connection" );
????????????? if( dataset == null ) throw new argumentnullexception( "dataset" );
????????????? sqlcommand command = new sqlcommand();
????????????? bool mustcloseconnection = false;
????????????? preparecommand(command, connection, transaction, commandtype, commandtext, commandparameters, out mustcloseconnection );
??? ????????? using( sqldataadapter dataadapter = new sqldataadapter(command) )
????????????? {
???????? ????????? if (tablenames != null && tablenames.length > 0)
?????????????????? {
?????????????????????? string tablename = "table";
?????????????????????? for (int index=0; index < tablenames.length; index++)
?????????????????????? {
??????????????????????????? if( tablenames[index] == null || tablenames[index].length == 0 )
???????????????????????????????? throw new argumentexception( "the tablenames parameter must contain a list of tables, a value was provided as null or empty string.", "tablenames" );
??????????????????????????? tablename += (index + 1).tostring();//这里出现错误
?????????????????????? }
?????????????????? }
?????????????????? dataadapter.fill(dataset);
?????????????????? command.parameters.clear();
????????????? }
????????????? if( mustcloseconnection )
?????????????????? connection.close();
???????? }
?
这里把tablename += (index + 1).tostring();修改成
dataadapter.tablemappings.add((index>0)?(tablename+index.tostring()):tablename, tablenames[index]);就能解决问题。
?
接下来看看窗体程序的代码:
public class form1 : system.windows.forms.form
???? {
???????? private dataaccess _dataaccess;
???????? private datasetorders _ds;
???????? //……
???????? //构造函数
???????? public form1()
???????? {
????????????? initializecomponent();
????????????? _dataaccess = new dataaccess();
????????????? _ds = new datasetorders();
????????????? _ds.enforceconstraints = false; //关闭约束检查,提高数据填充效率
????????????? this.datagridcustomers.datasource = _ds;
????????????? this.datagridcustomers.datamember = _ds.customers.tablename;
????????????? this.datagridorders.datasource = _ds;
????????????? this.datagridorders.datamember = _ds.customers.tablename+"."+_ds.customers.childrelations[0].relationname;
????????????? this.datagridorderdetails.datasource = _ds;
????????????? this.datagridorderdetails.datamember = _ds.customers.tablename+"."+_ds.customers.childrelations[0].relationname+"."+_ds.orders.childrelations[0].relationname;
???????? }
对于上面的三个表的动态关联,你也可以使用setdatabinding方法来完成数据的动态绑定,而不是分别指定datagride的datasource和datamemger属性。
this.datagridcustomers.setdatabinding(_ds,_ds.customers.tablename);
this.datagridorders.setdatabinding(_ds,_ds.customers.tablename+"."+_ds.customers.childrelations[0].relationname);
this.datagridorderdetails.setdatabinding(_ds,_ds.customers.tablename+"."+_ds.customers.childrelations[0].relationname+"."+_ds.orders.childrelations[0].relationname);
}
数据填充事件处理如下:????????????????????????
private void buttonfilldata_click(object sender, system.eventargs e)
???????? {
????????????? _ds.clear();//重新填充数据集
????????????? _dataaccess.fillcustomerordersinfo(_ds);
????????????? //_dataaccess.fillcustomerordersinfowithsqlhelper(_ds);
???????? }
执行上面的事件处理函数我们会看到数据显示到对应的datagrid上,如(图2-1)所示。
如果使用数据读取器获取多表纪录下面是实现的一种方式(参考):
sqlcommand comm = new sqlcommand("getcustomerordersinfo",_conn);
comm.commandtype = commandtype.storedprocedure;
_conn.open();
sqldatareader reader = comm.executereader();
do
{
???? while(reader.read())
???? {
???????? console.writeline(reader[0].tostring());//获取数据代码
???? }
}while(reader.nextresult());
console.readline();
_conn.close();
?
?
