处理数据集

数据集(Dataset)对象包括DataTableCollection、DataRelationCollection对象。

DataTableCollection对象包含一或多个DataTable对象。DataTable对象又是由DataRowCollection、DataColumnCollection、ConstraintCollection对象组成。

现在,我们来看DataTable对象。

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.

  1. 在加载DataRow对象时,它仅包含Current版本的数据.
  2. 调用BeginEdit方法时,使DataRow对象进入编辑模式,此时,数据保存在Current和Proposed两个版本中.
  3. 执行EndEdit方法时,Current版本数据变为Original版本的数据,Proposed版本数据变为Current版本数据,而Proposed版本数据不存在.
  4. 执行EndEdit方法后,DataRow对象将包含Original和Current两种版本的数据.
  5. 如果再次调用BeginEdit方法,则Current版本的数据将复制为Proposed版本的数据.
  6. 如果此时再次调用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对象

DataView对象是DataTable对象提供的另一个窗口,可以存储和过滤DataTable中的数据,也可以为一个DataTable对象提供多个DataView对象.

DataView对象有以下常用属性:

属性 描述
RowFilter 获取或设置用于筛选DataView对象中数据的表达式
Sort 获取或设置用于对DataView对象中数据进行排序的表达式

例:

向Employee对象中添加以下数据,然后分别进行筛选和排序

EmployeeNo EmployeeName EmployeeAge EmployeeDeparment EmployeeScore
1 张三 30 3 100
2 李四 20 1 95
3 王五 31 2 80
4 赵六 34 3 92
5 刘七 30 1 91

添加数据及筛选、排序代码如下:

//为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对象

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/p/1824682.html