数据集(Dataset)对象包括DataTableCollection、DataRelationCollection对象。
DataTableCollection对象包含一或多个DataTable对象。DataTable对象又是由DataRowCollection、DataColumnCollection、ConstraintCollection对象组成。
现在,我们来看DataTable对象。
DataTable对象将表格化数据表示为内存中的一个包含行、列、约束的表。通过创建DataTable类的一个实例,可以向其中添加、删除、修改数据.
现在,我们首先创建一个Employee的表对象,然后向该表中添加列,并设置主键,随后,向其中添加数据.
创建Employee表对象
//创建DataTable对象 DataTable Employee = new DataTable("Employee");为Employee表添加EmployeeNo、EmployeeName、EmployeeAge、EmployeeDepartmentNo、EmployeeScore列:
DataColumn EmployeeNo = new DataColumn("EmployeeNo", typeof(string)); EmployeeNo.Unique = true; EmployeeNo.AllowDBNull = false; Employee.Columns.Add(EmployeeNo); DataColumn EmployeeName = new DataColumn("EmployeeName", typeof(string)); EmployeeName.AllowDBNull = false; Employee.Columns.Add(EmployeeName); DataColumn EmployeeAge = new DataColumn("EmployeeAge", typeof(Int32)); Employee.Columns.Add(EmployeeAge); DataColumn EmployeeDepartmentNo = new DataColumn("EmployeeDepartmentNo", typeof(string)); Employee.Columns.Add(EmployeeDepartmentNo); DataColumn EmployeeScore = new DataColumn("EmployeeScore", typeof(decimal)); Employee.Columns.Add(EmployeeScore);为Employee表设置主键
一个DataTable对象的主键是由一个或多个列组成的,用于唯一标识每个数据行.
在上面的代码中,我们可以将EmployeeNo设置为该表的主键.
//注意: // 因为表的主键可能是由一个或多个列组成的,所以该属性实际上是一个列的数组 Employee.PrimaryKey = new DataColumn[] { EmployeeNo };向Employee表中添加数据
//向Employee表中添加第一条数据 DataRow rowZhangSan = Employee.NewRow(); rowZhangSan["EmployeeNo"] = "1"; rowZhangSan["EmployeeName"] = "张三"; rowZhangSan["EmployeeAge"] = 30; rowZhangSan["EmployeeDepartmentNo"] = "1"; rowZhangSan["EmployeeScore"] = 100; Employee.Rows.Add(rowZhangSan); //向Employee表中添加第二条数据 Employee.Rows.Add("2", "李四", 25, "2", 90); //向Employee表中添加第三条数据 Employee.LoadDataRow(new object[] { "1", "王五", 28, "3", 95 }, LoadOption.OverwriteChanges);使用DataRowState查看DataRow对象的状态
//输出结果: //张三 Unchanged //李四 Unchanged //王五 Unchanged foreach (DataRow row in Employee.Rows) { Response.Write(row["EmployeeName"].ToString().PadRight(5,' ') + row.RowState.ToString() + "<br/>"); }此处介绍一下DataRowState.DataRow对象包含一系列状态,可以在任何时候查看并筛选这些状态.
通过DataRow.RowState属性可以获取DataRow对象的当前状态,该属性包含一个DataRowState枚举.该枚举取值如下:
RowState的值描述Detached已经创建了DataRow对象,但是还没有将其添加到DataTable中的DataRow对象的状态Added已经创建了DataRow对象,并已经将其加入到DataTable中的DataRow对象的状态Unchanged自上一次调用AccepChanges方法后,还没有修改的DataRow对象的状态.调用AccepChanges方法,该DataTable中的所有Row都变为Unchanged状态.Modified自上一次调用AcceptChanges方法后,已经修改的DataRow对象的状态Deleted使用DataRow类的Delete方法删除的DataRow对象的状态.
我们接着"为Employee表设置主键"代码段后,重新添加数据.添加代码如下:
//创建DataRow对象 DataRow rowZhangSan = Employee.NewRow(); rowZhangSan["EmployeeNo"] = "1"; rowZhangSan["EmployeeName"] = "张三"; rowZhangSan["EmployeeAge"] = 30; rowZhangSan["EmployeeDepartmentNo"] = "1"; rowZhangSan["EmployeeScore"] = 100; //结果如下: // Detached DisplayRowState(rowZhangSan); //向Employee表中添加数据rowZhangSan //结果如下: // Added DisplayRowState(Employee.Rows.Add, rowZhangSan); //调用AcceptChanges方法 //结果如下: // Unchanged DisplayRowState(Employee.AcceptChanges, rowZhangSan); rowZhangSan["EmployeeScore"] = 90; //结果如下: // Modified DisplayRowState(rowZhangSan); //回滚至上一次加载后的结果 //结果如下: // Unchanged DisplayRowState(Employee.RejectChanges, rowZhangSan); //删除该行 //结果如下: // Deleted DisplayRowState(rowZhangSan.Delete, rowZhangSan);其中使用了方法DisplayRowState,其代码如下:
void DisplayRowState(DataRow row) { Response.Write(row.RowState.ToString() + "<br/>"); } void DisplayRowState(Action action,DataRow row) { action(); this.DisplayRowState(row); } void DisplayRowState(Action<DataRow> action, DataRow row) { action(row); this.DisplayRowState(row); }我们看到,上面的例子中有一个RejectChanges方法,该方法可以将数据回滚至上次AcceptChanges之后的数据状态.
那么,我们能否这样调用Employee.RejectChanges().RejectChanges()来回滚至上上次的AcceptChanges之后的数据状态呢.
答案是不行的,这就关系到了DataRow对象的三个版本的数据.请看下面的"使用DataRowVersion管理数据的多个版本".
使用DataRowVersion管理数据的多个版本
DataRow对象包含以下三种版本的数据:Original,Current和Proposed.
在加载DataRow对象时,它仅包含Current版本的数据. 调用BeginEdit方法时,使DataRow对象进入编辑模式,此时,数据保存在Current和Proposed两个版本中. 执行EndEdit方法时,Current版本数据变为Original版本的数据,Proposed版本数据变为Current版本数据,而Proposed版本数据不存在. 执行EndEdit方法后,DataRow对象将包含Original和Current两种版本的数据. 如果再次调用BeginEdit方法,则Current版本的数据将复制为Proposed版本的数据. 如果此时再次调用EndEdit方法,则将使Proposed版本数据变为Current版本的数据.从DataRow对象获取数据时,可以指定DataRowVersion的值,来获取相应的值.
取值描述Original为原先加载到DataRow对象中的值,或上一次执行了AcceptChanges方法时的值.Current在数据已经发生变化后DataRow对象的当前值.除非DataRow对象的RowState=Deleted,否则该版本的数据在任何时候都存在Proposed在编辑DataRow对象时的值.Default表示默认版本.处于Added,Modified,UnChanged状态行的默认版本是Current处于Deleted状态行的默认版本是Original处于Detached状态行的默认版本是Proposed我们继续"为Employee表设置主键"段向后,继续添加代码如下:
Employee.LoadDataRow(new object[] { "1", "张三", 30, "1", 100 }, LoadOption.PreserveChanges); DataRow rowZhangSan = Employee.Rows[0]; //LoadDataRow方法后 // EmployeeName:张三 RowState:Unchanged Version:Original // EmployeeName:张三 RowState:Unchanged Version:Current // Proposed不存在! // EmployeeName:张三 RowState:Unchanged Version:Default DisplayRowVersion("LoadDataRow方法后", rowZhangSan); rowZhangSan.BeginEdit(); //调用BeginEdit方法,但未修改数据 // EmployeeName:张三 RowState:Unchanged Version:Original // EmployeeName:张三 RowState:Unchanged Version:Current // EmployeeName:张三 RowState:Unchanged Version:Proposed // EmployeeName:张三 RowState:Unchanged Version:Default DisplayRowVersion("调用BeginEdit方法,但未修改数据", rowZhangSan); rowZhangSan["EmployeeName"] = "李四"; //调用BeginEdit方法后 // EmployeeName:张三 RowState:Unchanged Version:Original // EmployeeName:张三 RowState:Unchanged Version:Current // EmployeeName:李四 RowState:Unchanged Version:Proposed // EmployeeName:李四 RowState:Unchanged Version:Default DisplayRowVersion("调用BeginEdit方法后", rowZhangSan); rowZhangSan.EndEdit(); //调用EndEdit方法后 // EmployeeName:张三 RowState:Modified Version:Original // EmployeeName:李四 RowState:Modified Version:Current // Proposed不存在! // EmployeeName:李四 RowState:Modified Version:Default DisplayRowVersion("调用EndEdit方法后", rowZhangSan); Employee.AcceptChanges(); //调用AcceptChanges方法后 // EmployeeName:李四 RowState:Unchanged Version:Original // EmployeeName:李四 RowState:Unchanged Version:Current // Proposed不存在! // EmployeeName:李四 RowState:Unchanged Version:Default DisplayRowVersion("调用AcceptChanges方法后", rowZhangSan); rowZhangSan["EmployeeName"] = "王五"; //修改EmployeeName=王五之后 // EmployeeName:李四 RowState:Modified Version:Original // EmployeeName:王五 RowState:Modified Version:Current // Proposed不存在! // EmployeeName:王五 RowState:Modified Version:Default DisplayRowVersion("修改EmployeeName=王五之后", rowZhangSan); Employee.RejectChanges(); //调用RejectChanges后 // EmployeeName:李四 RowState:Unchanged Version:Original // EmployeeName:李四 RowState:Unchanged Version:Current // Proposed不存在! // EmployeeName:李四 DisplayRowVersion("调用RejectChanges后", rowZhangSan); rowZhangSan.Delete(); //删除该行后 // EmployeeName:李四 RowState:Deleted Version:Original // Current不存在! // Proposed不存在! // Default不存在! DisplayRowVersion("删除该行后", rowZhangSan);使用DisplayRowVersion代码如下:
void DisplayRowVersion(string state, DataRow row) { Response.Write("<hr/>" + state + "<br/>"); foreach (string version in Enum.GetNames(typeof(DataRowVersion))) { DataRowVersion rowVersion = (DataRowVersion)Enum.Parse(typeof(DataRowVersion), version); try { if (row.HasVersion(rowVersion)) { Response.Write(String.Format(" EmployeeName:{0} RowState:{1} Version:{2}<br/>", row["EmployeeName", rowVersion], row.RowState, version)); } else { Response.Write(string.Format(" {0}不存在!<br/>", version)); } } catch (DeletedRowInaccessibleException e) { Response.Write(string.Format(" RowState:{0} Version{1} {2}<br/>", row.RowState, version, e.Message)); } } }现在,我们来看RejectChanges方法调用之后的状态:
Employee.RejectChanges(); //调用RejectChanges后 // EmployeeName:李四 RowState:Unchanged Version:Original // EmployeeName:李四 RowState:Unchanged Version:Current // Proposed不存在! // EmployeeName:李四 DisplayRowVersion("调用RejectChanges后", rowZhangSan);RejectChanges方法可以将Original版本的数据变为Current版本的数据.
上例中调用了RejectChanges方法后,Original版本的数据和Current版本的数据一致.
此时,如果再次调用RejectChanges方法只是将Original版本数据复制到Current版本中,数据无法至再前一个状态的数据.
这也就解答了上面的问题:无法通过调用Employee.RejectChanges().RejectChanges()来回滚至上上次的AcceptChanges之后的数据状态.
DataView对象是DataTable对象提供的另一个窗口,可以存储和过滤DataTable中的数据,也可以为一个DataTable对象提供多个DataView对象.
DataView对象有以下常用属性:
属性描述RowFilter获取或设置用于筛选DataView对象中数据的表达式Sort获取或设置用于对DataView对象中数据进行排序的表达式例:
向Employee对象中添加以下数据,然后分别进行筛选和排序
EmployeeNoEmployeeNameEmployeeAgeEmployeeDeparmentEmployeeScore1张三3031002李四201953王五312804赵六343925刘七30191添加数据及筛选、排序代码如下:
//为Employee表添加数据 Employee.Rows.Add("1", "张三", 30, "3", 100); Employee.Rows.Add("2", "李四", 20, "1", 95); Employee.Rows.Add("3", "王五", 31, "2", 80); Employee.Rows.Add("4", "赵六", 34, "3", 92); Employee.Rows.Add("5", "刘七", 30, "1", 91); DataView dv = Employee.DefaultView; //对DataView对象进行排序 dv.Sort = "EmployeeAge desc,EmployeeScore desc"; //对DataView对象进行筛选 dv.RowFilter = "EmployeeAge>20 and EmployeeScore>80"; //输出结果如下: // 赵六 34 92 // 张三 30 100 // 刘七 30 91 foreach (DataRowView rowView in dv) { Response.Write(rowView["EmployeeName"] + " " + rowView["EmployeeAge"] + " " + rowView["EmployeeScore"] + "<br/>"); }DataRelation对象用于关联同一个DataSet对象中的多个DataTable对象.
在关联DataTable对象时,将创建一条从一个DataTable对象到另一个DataTable对象的路径.
通过编程的方法,可以从父DataTable对象遍历至子DataTable对象,或者从子Datatable对象遍历到父DataTable对象,这样就实现了DataTable对象间的导航.
下面,以例子的形式来理解DataRelation.
我们已经有了一个雇员表,下面,我们再建立一个部门(Deparment)表.表中有DepartmentNo和DepartmentName列,其中DepartmentNo列是主键,并与Employee表中的DepartmentNo关联.本例完整代码如下:
//创建Employee表对象 DataTable Employee = new DataTable("Employee"); //为Employee表添加EmployeeNo、EmployeeName、EmployeeAge、EmployeeDepartmentNo、EmployeeScore列 DataColumn EmployeeNo = new DataColumn("EmployeeNo", typeof(string)); EmployeeNo.Unique = true; EmployeeNo.AllowDBNull = false; Employee.Columns.Add(EmployeeNo); DataColumn EmployeeName = new DataColumn("EmployeeName", typeof(string)); EmployeeName.AllowDBNull = false; Employee.Columns.Add(EmployeeName); DataColumn EmployeeAge = new DataColumn("EmployeeAge", typeof(Int32)); Employee.Columns.Add(EmployeeAge); DataColumn EmployeeDeparmentNo = new DataColumn("EmployeeDeparmentNo", typeof(string)); Employee.Columns.Add(EmployeeDeparmentNo); DataColumn EmployeeScore = new DataColumn("EmployeeScore", typeof(decimal)); Employee.Columns.Add(EmployeeScore); Employee.PrimaryKey = new DataColumn[] { EmployeeNo }; //为Employee表添加数据 Employee.Rows.Add("1", "张三", 30, "3", 100); Employee.Rows.Add("2", "李四", 20, "1", 95); Employee.Rows.Add("3", "王五", 31, "2", 80); Employee.Rows.Add("4", "赵六", 34, "3", 92); Employee.Rows.Add("5", "刘七", 30, "1", 91); //创建Department表 DataTable Department = new DataTable("Department"); //添加DepartmentNo列 DataColumn DepartmentNo = new DataColumn("DepartmentNo", typeof(string)); DepartmentNo.AllowDBNull = false; DepartmentNo.Unique = true; Department.Columns.Add(DepartmentNo); //添加DepartmentName列 DataColumn DepartmentName = new DataColumn("DepartmentName", typeof(string)); Department.Columns.Add(DepartmentName); //为Department表设置主键 Department.PrimaryKey = new DataColumn[] { DepartmentNo }; //为Department表添加数据 Department.Rows.Add("1", "生产部"); Department.Rows.Add("2", "科技部"); Department.Rows.Add("3", "销售部"); //因为DataRelation是同一个DataSet对象中的两个表之间的关联情况 //所以需创建一个DataSet对象 System.Data.DataSet ds = new System.Data.DataSet("ds"); //将Employee表添加入DataSet对象 ds.Tables.Add(Employee); //将Department表添加入DataSet对象 ds.Tables.Add(Department); //添加主外键关系 ds.Relations.Add("Department_Employee", Department.Columns["DepartmentNo"], Employee.Columns["EmployeeDeparmentNo"]); //根据Department遍历到Employee //输出结果如下: // 生产部 // 李四 // 刘七 // 科技部 // 王五 // 销售部 // 张三 // 赵六 foreach (DataRow row in Department.Rows) { Response.Write(row["DepartmentName"].ToString() + "<br/>"); foreach (DataRow r in row.GetChildRows("Department_Employee")) { Response.Write(" " + r["EmployeeName"] + "<br/>"); } } //根据Employee遍历到Department //输出结果: // 张三 销售部 // 李四 生产部 // 王五 科技部 // 赵六 销售部 // 刘七 生产部 foreach (DataRow row in Employee.Rows) { DataRow r = row.GetParentRow("Department_Employee"); Response.Write(row["EmployeeName"] + " " + r["DepartmentName"] + "<br/>"); }创建DataTable对象
查看DataRow.RowState
使用DataRowVersion管理数据的多个版本
通过DataView对象对数据进行筛选和排序
使用DataRelation对象加强表之间的关系
转载于:https://www.cnblogs.com/oneword/archive/2010/09/13/1824682.html