6.9 DataSet类
DataSet是非连接数据访问的核心,它包含两类最重要的元素:零个或者多个表的集合(通过DataTable属性提供。也就是说,DataSet.DataTable集合里的每一个项目是一个DataTable)以及零个或者多个关系的集合(通过Relations属性提供),关系可以把表联系在一起。
6.9.1 使用DataAdapter填充DataSet
其实,使用DataAdapter填充DataSet的方法与填充DataTable一样,你要做的就是创建一个新的空DataSet对象(在创建DataSet对象时,可以选择指定一个名称参数。如果没有为DataSet指定名称,则该名称会设置为“NewDataSet”),然后使用DataAdapter对象的Fill()方法来执行查询,并将查询的结果放到DataSet中新建的DataTable里。在Fill()方法中,可以指定表名,也可以不指定表名。如果不指定表名,系统会采用默认的表名。
下面的示例演示了如何使用DataAdapter来填充一个DataSet:
public DataSet GetEmployee()
{
string connectionString=
WebConfigurationManager.ConnectionStrings
["ConnectionString"].ConnectionString;
SqlConnection con=new SqlConnection(connectionString);
SqlDataAdapter adapter=new SqlDataAdapter(
"select*from Employee",con);
DataSet ds=new DataSet();
using(con)
{
adapter.Fill(ds);//或者adapter.Fill(ds,"Employee");
}
return ds;
}
填充好DataSet之后,就可以使用ds.Tables[0];或者ds.Tables["Employee"];的形式来获取DataSet里的DataTable来进行处理。如下面的代码所示:
protected void Page_Load(object sender, EventArgs e)
{
DataSet ds=GetEmployee();
DataTable myTable=ds.Tables[0];
//或者DataTable myTable=ds.Tables["Employee"];
foreach(DataColumn column in myTable.Columns)
{
Label1.Text+=column.ColumnName
+"      ";
}
Label1.Text+="RowState<br/>";
foreach(DataRow rows in myTable.Rows)
{
foreach(DataColumn column in myTable.Columns)
{
Label1.Text+=rows[column].ToString()
+"  ";
}
Label1.Text+=rows.RowState.ToString()+"<br>";
}
}
示例运行结果如图6-32所示。
6.9.2 使用多个表和关系
为了演示的需要,下面在数据库ASPNET4里添加一张工资表Salary。其中,Salary表与Employee表通过employeeid字段来建立关系。Salary表结构如图6-33所示。
图 6-32 使用DataAdapter填充DataSet的示例运行结果
图 6-33 数据库ASPNET4里的Salary表结构
下面的示例演示了如何从ASPNET4数据库里同时检索Salary表与Employee表以及如何在两表之间建立关联关系。
首先,需要使用DataAdapter将Salary表与Employee表填充到DataSet,如下面的代码所示:
public DataSet GetEmployee()
{
string connectionString=
WebConfigurationManager.ConnectionStrings
["ConnectionString"].ConnectionString;
string employeeSql="select*from Employee";
string salarySql="select*from Salary";
SqlConnection con=new SqlConnection(connectionString);
SqlDataAdapter adapter=new SqlDataAdapter(employeeSql, con);
DataSet ds=new DataSet();
using(con)
{
con.Open();
//将Employee表填充到DataSet
adapter.Fill(ds,"Employee");
//将Salary表填充到DataSet
adapter.SelectCommand.CommandText=salarySql;
adapter.Fill(ds,"Salary");
}
//将Salary与Employee通过employeeid来建立关系
DataRelation rel=new DataRelation("EmployeeSalary",
ds.Tables["Employee"].Columns["employeeid"],
ds.Tables["Salary"].Columns["employeeid"]);
ds.Relations.Add(rel);
return ds;
}
值得注意的是,在上面的代码中只创建一个DataAdapter对象来将两张表填充到了同一个DataSet中,这样的设计可以重用DataAdapter去更新数据源,从而避免了重复创建DataAdapter对象,提高程序的执行效率。并且,这里使用了显示打开连接的方式使整个操作完毕时才关闭连接,这尽可能地保证了最佳的性能。
表Salary与Employee的关系通过定义一个DataRelation对象并把它加入到DataSet.Relations集合来创建。创建DataRelation对象时,需要提供构造函数的三个参数:关系名称((EployeeSalary)、父表中作为主键的DataColumn(Employee的employeeid)与子表中作为外键的DataColumn(Salary的employeeid)。
对于这种关联关系,可以通过DataRow的GetChildRows()方法来获取相关的关联记录。如下面的代码所示:
DataRow[]childRows=row.GetChildRows("EmployeeSalary");
该方法在链接的DataTable中查找内存中的数据以查找匹配的记录,到相关的关联数据之后,就可以通过内嵌循环来查看它们了。示例如下面的代码所示:
protected void Page_Load(object sender, EventArgs e)
{
DataSet ds=GetEmployee();
StringBuilder str=new StringBuilder();
foreach(DataRow row in ds.Tables["Employee"].Rows)
{
str.Append("<b>");
str.Append(row["employeename"].ToString());
str.Append("</b><ul>");
//获取关联数据
DataRow[]childRows=row.GetChildRows("EmployeeSalary");
foreach(DataRow childRow in childRows)
{
str.Append("<li>");
str.Append("总工资:");
str.Append(childRow["total"].ToString());
str.Append("    应交税款:");
str.Append(childRow["salestax"].ToString());
str.Append("    实际工资:");
str.Append(childRow["salary"].ToString());
str.Append("</li>");
}
str.Append("</ul>");
}
Label1.Text=str.ToString();
}
示例运行结果如图6-34所示。
图 6-34 示例运行结果
值得注意的是,在DataSet中添加一个关系之后,就会受到引用完整性的约束。例如,不能够删除一个有连接子行的父记录,同时也不能够创建一个引用不存在的父记录的子记录。这样,当DataSet只包含部分数据时就会产生一些问题。而解决这个问题的一个办法就是创建一个没有约束的DataRelation,使用DataRelation的一个接受createConstraints参数的构造函数就可以满足此要求,如下面的代码所示:
DataRelation rel=new DataRelation("EmployeeSalary",
ds.Tables["Employee"].Columns["employeeid"],
ds.Tables["Salary"].Columns["employeeid"],
false);
当然,也可以在添加关系前将DataSet的EnforceConstraints属性设置为false,从而禁止所有类型的约束检查,包括唯一性检查。