LINQ to DataSet实现复杂数据查询

mac2022-06-30  22

5.2 LINQ to DataSet实现复杂数据查询

LINQ to DataSet LINQ ADO.NET集成,它通过 ADO.NET获取数据,然后通过 LINQ进行数据查询,从而实现对数据集进行非常复杂查询。本节将介绍如何使用 LINQ to DataSet操作数据集 DataSet中的数据。

5.2.1   使用LINQ to DataSet

LINQ to DataSet可以简单理解成通过 LINQ DataSet中保存的数据进行查询,它和第 7章介绍的 LINQ查询并没有太大的区别。 LINQ to DataSet的使用通常包含以下步骤:

1)获取 DataSet/DataTable数据源。 LINQ to DataSet通过 LINQ查询 DataSet/DataTable中的数据,所以首先要准备 DataSet/DataTable数据源,可以通过 ADO.NET技术从数据库获取,可以通过 XML技术从 XML文件获取,也可以从其他任何形式的数据源获取,甚至可以在内存中直接创建并填充 DataSet/DataTable对象。

2)将 DataTable转换成 IEnumerable<T>类型。从第 7章了解到, LINQ只能在 IEnumerable<T> IQueryable<T>接口对象上执行查询操作,而 DataTable并没有实现这两个接口,不能直接查询。在 LINQ to DataSet中,通过 DataTableExtensions扩展的 AsEnumerable()方法从 DataTable获取一个等价的 IEnumerable<T>对象。

3)使用 LINQ语法编写查询。 LINQ to DataSet中查询的编写可以使用查询语法和方法语法,可以对它执行任何 IEnumerable<T>允许的查询操作。

4)使用查询结果。查询结果产生后,就可以使用查询结果(一个 IEnumerable<T>对象),比如,用 foreach遍历所有元素,用 Max()等进行数值计算,将它作为数据源进行二次查询等。

后面几个小节将通过实例详细介绍 LINQ to DataSet的具体使用,但是为了更加容易理解,这些示例中的 DataSet都通过代码直接在内存中编写,并不从数据库获取。

注意:由于 DataSet本身是 DataTable的集合,它可以包含一个或多个 DataTable及它们之间的关系, LINQ to DataSet实际是对 DataTable进行数据查询,并非对 DataSet进行查询。

5.2.2   查询单个数据表

一个 DataSet通常包含一个或多个 DataTable,同时也包括它们之间的关系集合等,实际上可以把它看成是一个缩影的数据库。 LINQ to DataSet也是对一个或多个 DataTable进行查询,这些 DataTable可以来自单个 DataSet,也可以是来自多个 DataSet

5.2.1节介绍了查询 DataTable中元素的主要步骤,在对 DataTable进行数据查询时必须使用 DataTable类的 AsEnumerable()方法,该方法将 DataTable转换成一个类型为 IEnumerable<DataRow>的可枚举数据集合,它的定义如下:

public static EnumerableRowCollection<DataRow> AsEnumerable(

    this DataTable source)

因此,从 DataTable中获取的元素类型为 DataRow,要进一步访问数据表的记录的具体字段数据,就需要使用 DataRow的一个扩展泛型方法—— Field<T>(),通过它获取 DataRow的某字段的数据,它包括 6个重载版本,其中最常用的是下面 3个。

public static T Field<T>( this DataRow row, DataColumn column )

public static T Field<T>( this DataRow row, int columnIndex )

public static T Field<T>( this DataRow row, string columnName )

其中,参数 column表示数据列( DataColumn),表示要返回数据的字段。参数 columnIndex表示从 0开始的索引列索引。 columnName表示要返回数据的字段的名称。通常为了让代码更加通用,作者建议尽量使用字段名称来指定要返回的字段。

在示例代码 5-1中,方法 BuildOneDTDataSet()在内存中创建一个名为“ PeopleDS”的数据集合,它只包含一个名为“ PeopleDT”的数据表,数据表包含 3个字段:姓名( Name)、性别( XingBie)、年龄( Age)。在方法 UseSelect()中,首先通过 BuildOneDTDataSet()创建数据集,然后通过 DataSet.Tables属性获取名为“ PeopleDT”的数据表。在查询 query1 query2中通过 DataTable.AsEnumerable()方法将 DataTable转换成 IEnumerable<T>类型的数据集合,并进行查询。 query1查询所有元素,而 query2只查询姓名字段。

示例代码 5-1

//随机创建一个包含数据的 DataSet

static DataSet BuildOneDTDataSet( )

{

    //可选姓名、性别和年龄,用于创建学生数据到数据表中

    string[] nameSet = {"王霞 ","张三 ","李四 ","李花 ","王五 ", "陆六 ","夏七 ","吴八 " };

    string[] xbSet = { " ", " ", " ", " ", " ", " ", " ", " " };

    int[] ageSet = {18, 20, 21, 22, 19, 20, 25, 24};

    DataSet ds = new DataSet("PeopleDS");             // 创建名为 PeopleDS DataSet对象

    DataTable dt = new DataTable("PeopleDT");    //创建名为 PeopleDT DataTable对象

    ds.Tables.Add(dt);                                                    //将数据表 dt添加到数据集 ds

    //创建 DataTable的列(字段)信息,包括 3个字段:

    //姓名: Name string类型

    //性别: XingBie string类型

    //年龄: Age int类型

    dt.Columns.AddRange(

        new DataColumn[]

        {

            new DataColumn("Name", Type.GetType("System.String")),

            new DataColumn("XingBie", Type.GetType("System.String")),

            new DataColumn("Age", Type.GetType("System.Int32")),

        });

    // 利用前面定义的可选姓名 nameSet、年龄 ageSet、性别 xbSet创建多个学生信息

    for (int i = 0; i < nameSet.Length; i++)

    {  

        //根据当前编号,自动新建数据表中的一行,并产生一行数据

        //然后通过 DataTable.Rows.Add()将这一行添加到数据表 dt

        DataRow row = dt.NewRow( );

        row["Name"] = nameSet[i];

        row["Age"] = ageSet[i];

        row["XingBie"] = xbSet[i];

        dt.Rows.Add(row);                                           //添加到数据表 dt

    }

    return ds;                                                                   // 返回 DataSet

}

static void UseSelect( )

{

    DataSet ds = BuildOneDTDataSet( );                   //获取数据集 ds

    DataTable dt = ds.Tables["PeopleDT"];                //从数据集 ds中获取名为“ PeopleDT”的数据表 dt

    //查询 query1表示查询 DataTable中所有记录,演示 AsEnumerable()的使用

    var query1 =

        from pl in dt.AsEnumerable( )

        select pl;

    System.Console.WriteLine("Query1:");

    foreach (var item in query1)                                   //打印查询 query1的结果

    {

        // 演示 Field<T>方法的使用

        System.Console.WriteLine("姓名 :{0},性别 :{1},年龄 :{2}",

            item.Field<string>("Name"), item.Field<string>("XingBie"), item.Field<int>("Age"));

    }

    //查询 query2表示查询 DataTable中所有人的姓名,演示 AsEnumerable() Field<T>的使用

    var query2 =

        from pl in dt.AsEnumerable( )

        select pl.Field<string>("Name");

    System.Console.WriteLine("Query2:");                // 打印查询 query1的结果

    foreach (var item in query2)

    {

        System.Console.Write("{0} ", item);

    }

    System.Console.WriteLine( );

}

示例代码 5-1的输出如下所示,其中,查询 query1的结果为表中所有完整记录,包括姓名、性别和年龄。查询 query2的结果只包括表中“ Name”字段的集合。

Query1:

姓名 :王霞,性别 :女,年龄 :18

姓名 :张三,性别 :男,年龄 :20

姓名 :李四,性别 :男,年龄 :21

姓名 :李花,性别 :女,年龄 :22

姓名 :王五,性别 :男,年龄 :19

姓名 :陆六,性别 :男,年龄 :20

姓名 :夏七,性别 :男,年龄 :25

姓名 :吴八,性别 :男,年龄 :24

Query2:

王霞 张三 李四 李花 王五 陆六 夏七 吴八

除了使用 select语句外,还可以对 DataTable记录进行 where过滤、 orderby排序、 groupby分组等操作。如示例代码 5-2所示,其中,查询 query3 query4中同时使用 orderby where子句,同时进行过滤和排序两个操作。 query3查询所有年龄大于 22岁的记录,并按照年龄从低到高排序。 query4查询所有年龄在 20~25之间的记录,并按照年龄从高到低排序。

示例代码 5-2

static void UseOrderByWhere( )

{

    DataSet ds = BuildOneDTDataSet( );                   //获取数据集 ds

    DataTable dt = ds.Tables["PeopleDT"];                //从数据集 ds中获取名为“ PeopleDT”的数据表 dt

    //查询 query3查询数据表中所有年龄大于 22的人,并且按照年龄从低到高排序

    var query3 =

        from pl in dt.AsEnumerable( )

        orderby pl.Field<int>("Age")

        where pl.Field<int>("Age") > 22

        select pl;

    System.Console.WriteLine("Query3:");

    foreach (var item in query1)                                   //打印查询 query3的结果

    {

        System.Console.WriteLine(" 姓名 :{0},性别 :{1},年龄 :{2}",

            item.Field<string>("Name"), item.Field<string>("XingBie"), item.Field<int>("Age"));

    }

    //查询 query4查询数据表中所有年龄大于 20小于 25的人,并且按照年龄从高到低排序

    var query4 =

        from pl in dt.AsEnumerable( )

        orderby pl.Field<int>("Age") descending

        where pl.Field<int>("Age") > 20

        where pl.Field<int>("Age") < 25

        select pl;

    System.Console.WriteLine("Query4:");

    foreach (var item in query2)                                   // 打印查询 query4的结果

    {

        System.Console.WriteLine(" 姓名 :{0},性别 :{1},年龄 :{2}",

            item.Field<string>("Name"), item.Field<string>("XingBie"), item.Field<int>("Age"));

    }

}

示例代码 5-2的输出如下所示,其中, query3输出是年龄大于 22岁的记录, query4输出是年龄在 20~25之间的记录。

Query3:

姓名 : 吴八,性别 : 男,年龄 :24

姓名 : 夏七,性别 : 男,年龄 :25

Query4:

姓名 : 吴八,性别 : 男,年龄 :24

姓名 : 李花,性别 : 女,年龄 :22

姓名 : 李四,性别 : 男,年龄 :21

技巧: LINQ to DataSet查询 DataTable的数据可以简单分成两个部分,首先是将 DataTable转换成 IEnumerable<T>数据集合,然后就是对 IEnumerable<T>进行操作,这一步可以完全应用第 7章介绍的所有 LINQ查询操作。

5.2.3   查询多个数据表

通常,一个数据集( DataSet)包含多个数据表( DataTable),而且数据表之间具有一定的关联关系,从而表示一个关系型数据库。通过 LINQ to DataSet同样可以轻松查询多个数据表中的数据,这通常需要使用多个 from子句进行复合查询,同时通过 where子句来进行多个表之间的关系判断。

本节的例子中,使用示例代码 5-3中创建的数据集合, BuildDataSet()方法创建一个名为 Students的数据表,包含两个数据表 Students Scores,前者记录学生信息,包括:姓名( Name)、性别( XingBie)、年龄( Age)、成绩号( ScoreID)。后者记录学生成绩,包括:成绩号( ScoreID)、数学成绩( Math)、语文成绩( Chinese)、英语成绩( English)。其中,字段成绩号是两个表关联字段,通过该字段可以查询学生的成绩信息。

示例代码 5-3

static DataSet BuildDataSet( )

{

    DataSet ds = new DataSet("Students");                         //创建 Students数据集

    //创建 Students数据表 dtStu,并添加到数据集 ds

    //Students数据表包含学生信息

    DataTable dtStu = new DataTable("Students");

    ds.Tables.Add(dtStu);

    //添加学生信息记录的列信息,包括 4列数据:

    //姓名: Name string类型

    //性别: XingBie string类型

    //年龄: Age int类型

    //成绩编号: ScoreID int类型

    dtStu.Columns.AddRange(new DataColumn[]{

        new DataColumn("Name", Type.GetType("System.String")),

        new DataColumn("XingBie", Type.GetType("System.String")),

        new DataColumn("Age", Type.GetType("System.Int32")),

        new DataColumn("ScoreID", Type.GetType("System.Int32")),

    });

    //添加 5个学生信息到数据表 dtStu中,分别包括姓名、性别、年龄和成绩编号

    dtStu.Rows.Add("张三 ", " ", 20, 1);

    dtStu.Rows.Add("李四 ", " ", 19, 2);

    dtStu.Rows.Add("王霞 ", " ", 21, 3);

    dtStu.Rows.Add("赵敏 ", " ", 22, 4);

    dtStu.Rows.Add("吴安 ", " ", 18, 5);

    //创建 Scores数据表,并添加到数据集

    //Scores数据表包含学生成绩记录

    DataTable dtScore = new DataTable("Scores");

    ds.Tables.Add(dtScore);

    //添加成绩记录表的列(字段)信息,包含 4个字段:

    //成绩编号: ScoreID int类型,与 Students表的 ScoreID字段对应

    //数学成绩: Math int类型

    //语文成绩: Chinese int类型

    //英语成绩: English int类型

    dtScore.Columns.AddRange(new DataColumn[]{

        new DataColumn("ScoreID", Type.GetType("System.Int32")),

        new DataColumn("Math", Type.GetType("System.Int32")),

        new DataColumn("Chinese", Type.GetType("System.Int32")),

        new DataColumn("English", Type.GetType("System.Int32")),

    });

    //添加学生成绩记录,分别包括成绩编号、数学成绩、语文成绩、英语成绩

    dtScore.Rows.Add(1, 80, 75, 78);

    dtScore.Rows.Add(3, 88, 80, 60);

    dtScore.Rows.Add(4, 75, 90, 80);

    dtScore.Rows.Add(5, 59, 80, 75);

    return ds;                                                                              //返回数据集

}

查询多个数据表的数据通常通过多个 from子句进行联合查询,每个 from子句对应一个数据表,同时用 where子句来表示多个数据表之间的关系,一般单个 where子句表示两个表之间的关系。在进行多表数据查询之前,要明确几个问题:

1)要在哪些数据表中查询数据? from子句该如何编写?

2)查询结果包含哪些数据表的哪些字段? select子句该如何编写?

3)各数据表之间的关系如何进行关联? where子句该如何编写?

4)是否需要其他的操作,比如排序( orderby子句)、分组( group子句)等?

5)该查询是使用简单的单个查询实现,还是通过多个查询组合实现?

如示例代码 5-4所示,方法 QueryStuScores()中首先通过 BuildDataSet()方法获取数据集和要查询的数据表,其中 dtStu表示学生信息数据表, dtScore表示学生成绩数据表。查询 query1用于查询数据集合中所有学生的成绩,如果学生没有成绩则不作为结果返回。

query1中,第 1 from子句从表 dtStu中查询学生信息记录,并保存到临时变量 stu中。第 2 from子句从表 dtScore中查询成绩记录,并保存到临时遍历 score中。 Where子句则用于实现两个表之间的关联关系,即:成绩号( ScoreID)相等。最后 select子句则表示要将表 dtStu Name字段和 dtScore Math Chinese English字段作为查询结果。

示例代码 5-4

static void QueryStuScores( )

{

    DataSet ds = BuildDataSet( );                                                   //获取数据集 ds

    DataTable dtStu = ds.Tables["Students"];                               //从数据集 ds中获取 Students dtStu

    DataTable dtScore = ds.Tables["Scores"];                              //从数据集 ds中获取 Scores dtScore

    var query1 =                                                                                  //查询 query1查询所有学生的成绩

        from stu in dtStu.AsEnumerable( )                                   // Students表和 Scores表中查询

        from score in dtScore.AsEnumerable( )

        where stu.Field<int>("ScoreID") == score.Field<int>("ScoreID") // 成绩编号( ScoreID)相等

        select new                                                                             //匿名类型为查询结果元素类型,包括四个成员

        {

            Name = stu.Field<string>("Name"),

            MathS = score.Field<int>("Math"),

            Chinese = score.Field<int>("Chinese"),

            English = score.Field<int>("English")

        };

    System.Console.WriteLine("Query1-所有学生成绩: ");

    foreach (var item in query1)                                                      //打印查询 query1的结果

    {

        System.Console.WriteLine(" 姓名 :{0}, 数学 :{1}, 语文 :{2}, 英语 :{3}",

            item.Name, item.MathS, item.Chinese, item.English);

    }

}

示例代码 5-4的输出如下,从中可以看出学生“李四”没有成绩,所以不在查询 query1的结果中。

Query1-所有学生成绩:

姓名 :张三 , 数学 :80, 语文 :75, 英语 :78

姓名 :王霞 , 数学 :88, 语文 :80, 英语 :60

姓名 :赵敏 , 数学 :75, 语文 :90, 英语 :80

姓名 :吴安 , 数学 :59, 语文 :80, 英语 :75

对于一些比较复杂的查询,仅使用一个 LINQ查询很难实现,这就需要使用多个查询配合使用。比如现在需要查询没有成绩的学生的信息,该查询可以有两种方法实现,但都需要通过多个查询配合实现。

如示例代码 5-5所示, query2查询采用第 1种方法,首先查询 scoreIDs得出表 dtScore中所有的成绩号集合,然后 query2在从表 dtStu中找出所有学生中,成绩号不在查询 scoreIDs中的学生,这些学生就是没有成绩的学生。

query3则采用第 2种方法,首先查询 scrStu通过两个并列的 from子句,从表 dtStu dtScore中查询所有具有成绩的学生集合,和示例代码 5-4中一样。然后 query3通过方法语法的形式,通过 Except()方法从所有学生信息中剔除具有成绩的学生,剩下就是没有成绩的学生。

示例代码 5-5

static void QueryNoneScoreStu( )

{

    DataSet ds = BuildDataSet( );                                //获取数据集 ds

    DataTable dtStu = ds.Tables["Students"];            //从数据集 ds中获取 Students dtStu

    DataTable dtScore = ds.Tables["Scores"];           //从数据集 ds中获取 Scores dtScore

    var scoreIDs =                                                           //查询 scoreIDs查询所有有成绩的学生的成绩编号

        from score in dtScore.AsEnumerable( )

        select score.Field<int>("ScoreID");

    var query2 =                                                               //查询 query2查询所有成绩号不在查询 scoreIDs中学生信息

        from stu in dtStu.AsEnumerable( )

        where !scoreIDs.Contains<int>(stu.Field<int>("ScoreID"))

        select stu;

    System.Console.WriteLine("Query2- 没有成绩的学生: ");

    foreach (var item in query2)                                   //打印查询 query2的结果

    {

        System.Console.WriteLine(" 姓名 :{0}, 性别 :{1}, 年龄 :{2}",

            item.Field<string>("Name"), item.Field<string>("XingBie"), item.Field<int>("Age"));

    }

    var scrStu =                                                                //查询 scrStu查询所有具有成绩信息的学生

        from stu in dtStu.AsEnumerable( )

        from score in dtScore.AsEnumerable( )

        where stu.Field<int>("ScoreID") == score.Field<int>("ScoreID")

        select stu;

    //查询 query3是从所有学生记录中剔除具有成绩的学生。

    var query3 = dtStu.AsEnumerable( ).Except(scrStu);

    System.Console.WriteLine("Query3- 没有成绩的学生: ");

    foreach (var item in query3)                                   //打印查询 query3的结果

    {

        System.Console.WriteLine(" 姓名 :{0}, 性别 :{1}, 年龄 :{2}",

            item.Field<string>("Name"), item.Field<string>("XingBie"), item.Field<int>("Age"));

    }

}

示例代码 5-5的输出如下所示,可以看出 query2 query3虽然实现的方法不同,但是最终产生的查询结果都是一样,都给出了没有成绩的学生“李四”的详细信息。

Query2-没有成绩的学生:

姓名 :李四 , 性别 : , 年龄 :19

Query3-没有成绩的学生:

姓名 :李四 , 性别 : , 年龄 :19

技巧:从示例代码 5-5 query2 query3可以看出,很多问题都可以有很多种不同的解决方法,但是最终结果都是一样,在解决问题时,也应该尽可能去寻找更多的解决办法,并从中选择最简单高效最适用的一种方法。另外,不要忘了方法语法在 LINQ中的使用。

5.2.4   用查询创建数据表

LINQ to DataSet通过 DataTableExtensions类提供的扩展方法 CopyToDataTable()将从数据表中获取到的查询结果(类型为 IEnumerable<DataRow>)直接复制到一个新的数据表( DataTable)中,从而可以将查询结果绑定到界面控件( DataGridView等),也可以使用一些 DataTable特有的特性。

CopyToDataTable()包括 3个重载版本,定义如下,其中第 1个版本最简单,也最常用。注意,这里的所有类型 T都是 DataRow类型及其子类。

public static DataTable CopyToDataTable<T>(

    this IEnumerable<T> source) where T : DataRow

public static void CopyToDataTable<T>(

    this IEnumerable<T> source,

    DataTable table,

    LoadOption options) where T : DataRow

public static void CopyToDataTable<T>(

    this IEnumerable<T> source,

    DataTable table,

    LoadOption options,

    FillErrorEventHandler errorHandler) where T : DataRow

其中, table表示目标数据表对象,用来保存数据。 options用于指定 DataTable的加载属性。 errorHandler是一个函数委托,开发人员可以指定自定义的异常处理操作。 CopyToDataTable()方法使用下面的过程通过查询创建 DataTable复本:

1 CopyToDataTable()方法克隆源表中的 DataTable(实现 IQueryable<T>接口的 DataTable对象)。 IEnumerable源通常来源于 LINQ to DataSet表达式或方法查询。

2)目标 DataTable的架构从源表中第一个 DataRow对象的列生成,克隆表的名称是源表的名称加上单词“ query”。

3)对于源表中的每一行,将行内容复制到新 DataRow对象中,然后将该对象插入到目标 DataTale中。

4)复制完源表中所有 DataRow对象后,返回克隆的 DataTable。如果源序列不包含任何 DataRow对象,则该方法将返回一个空 DataTable

示例代码 5-6演示了 CopyToDataTable()方法的使用,其中,查询 query1查询所有既有成绩,年龄又大于 20岁的学生信息,此时 query1类型为 IEnumerable<DataRow>。然后使用 query1 CopyToDataTable()方法创建一个 DataTable副本 newDt,最后打印出 newDt的数据。

示例代码 5-6

static void UseCopyToDTSimple( )

{

    DataSet ds = BuildDataSet( );                                //获取数据集 ds

    DataTable dtStu = ds.Tables["Students"];            //从数据集 ds中获取 Students dtStu

    DataTable dtScore = ds.Tables["Scores"];           //从数据集 ds中获取 Scores dtScore

    var query1 =                                                               //查询 query1年龄大于 20且具有成绩的学生

        from stu in dtStu.AsEnumerable( )

        from score in dtScore.AsEnumerable( )

        where stu.Field<int>("ScoreID") == score.Field<int>("ScoreID")

        where (int)stu["Age"] > 20

        select stu;

    // 通过 CopyToDataTable()方法创建新的副本     //然后打印该副本的信息

    DataTable newDt = query1.CopyToDataTable<DataRow>( );

    System.Console.WriteLine(" 学生列表: ");

    foreach (var item in newDt.AsEnumerable())     //打印该副本的信息

    {

        System.Console.WriteLine(" 姓名 :{0}, 性别 :{1} 年龄 :{2}",

            item["Name"], item["XingBie"], item["Age"]);

    }

}

示例代码 5-6的输出如下所示,可见 query1的副本 newDt所包含的数据和 query1完全相同,注意本例中的数据源是示例代码 5-3所生成。

学生列表:

姓名 :王霞 , 性别 :女, 年龄 :21

姓名 :赵敏 , 性别 :女, 年龄 :22

技巧:由于本例是控制台应用程序,所以只是简单打印 newDt的记录,实际开发中 CopyToDataTable()创建的副本,通常用于界面绑定。

5.2.5   修改表中字段数据

在前面章节的示例代码中,只是使用 DataRowExtensions.Field()方法来获取数据表中字段的数据,当然 LINQ to DataSet有时也需要对数据表中的数据进行修改,本节介绍如何使用 SetField()修改数据。

LINQ to DataSet中, DataRowExtensions类提供泛型扩展方法 SetField(),用于设置数据表中指定列的数据,并且指定明确的数据类型。 DataRowExtensions.SetField()方法具有 3个重载版本,它们的定义如下所示:

public static void SetField<T>(

    this DataRow row,

    DataColumn column,

    T value)

public static void SetField<T>(

    this DataRow row,

    int columnIndex,

    T value)

public static void SetField<T>(

    this DataRow row,

    string columnName,

    T value)

其中, column是表示要设置数据的列对象( DataColumn类型), columnIndex是从 0开始的要设置数据的列索引, columnName是要设置数据的列名称。通常,一个数据表的列名是固定不变的,所以作者建议尽可能使用列名指定要设置数据的列,这样的代码更具扩展性。

示例代码 5-7演示了 SetField()方法的使用,通过 BuildDataSet()方法获取数据表,第 1 foreach语句遍历数据表的记录。通过 SetField<int>()方法将学生的年龄增加 2岁,因为学生年龄是 int类型,所以 SetField<T>()中的 T int类型表示。最后,打印出数据表中所有的学生列表。

示例代码 5-7

static void UseSetField( )

{

    DataSet ds = BuildDataSet( );                                //获取数据集 ds

    DataTable dtStu = ds.Tables["Students"];            //从数据集 ds中获取 Students dtStu

    //遍历表 dtStu中所有的学生,并通过 SetField()方法将它们的年龄都增加 2岁。

    foreach (var row in dtStu.AsEnumerable())

    {

        int age = row.Field<int>("Age");

       row.SetField<int>("Age", age + 2);

    }

    System.Console.WriteLine(" 学生列表: ");

    foreach (var item in dtStu.AsEnumerable( ))      //打印修改年龄后的学生信息

    {

        System.Console.WriteLine(" 姓名 :{0}, 性别 :{1} 年龄 :{2}",

            item["Name"], item["XingBie"], item["Age"]);

    }

}

示例代码 5-7的输出如下所示,从中可以看出,所有学生的年龄都被增加了 2岁。

学生列表:

姓名 :张三 , 性别 :男, 年龄 :22

姓名 :李四 , 性别 :男, 年龄 :21

姓名 :王霞 , 性别 :女, 年龄 :23

姓名 :赵敏 , 性别 :女, 年龄 :24

姓名 :吴安 , 性别 :男, 年龄 :20

注意: SetField()方法是直接修改数据表中的记录,如果要保持原数据不变,那么在使用 SetField()之前,应该先备份源数据表,作者建议通过 CopyToDataTable()方法对源数据表进行备份。

5.2.6   使用数据视图DataView

数据视图( DataView)是常用的只读形式数据保存形式,可以将它绑定到 UI界面,为用户提供形象动态的数据查阅功能。 DataView本身可以从 DataTable DataSet获取,除此之外,在 .NET3.5中,还可以通过 LINQ查询来获取 DataView

LINQ to DataSet中,可以使用 DataTableExtensions.AsDataView()扩展方法从 DataTable LINQ查询创建一个与数据源对应的 DataView对象。 AsDataView()方法包括 2个重载版本,定义如下,它们只是扩展的目标类型不同(即:数据源不同)。

public static DataView AsDataView(

    this DataTable table

)

public static DataView AsDataView<T>(

    this EnumerableRowCollection<T> source

) where T : DataRow

AsDataView() 2个重载版本中,需要 EnumerableRowCollection<DataRow>类型的数据源,该类型表示从 LINQ查询返回的 DataRow对象集合,可以从 LINQ查询获取。

示例代码 5-8演示了 AsDataView()方法的使用。其中, BuildDataSet()只是用来创建数据源,不再赘述。 CreateDataView()方法中, dvDt是通过 DataTable类的扩展方法 AsDataView()创建 DataView,该 DataView中包含数据表中所有的数据记录。其中,查询 query1 query2 query3都是 LINQ to DataSet查询,并且明确类型为 EnumerableRowCollection<DataRow>,当然这里也可以用 var可变类型,编译器会自动转换。

示例代码 5-8

static DataSet BuildDataSet( )

{

    DataSet ds = new DataSet("Students");               //创建 Students数据集

    //创建 Students数据表,并添加到数据集

    //Students数据表包含学生信息

    DataTable dtStu = new DataTable("Students");

    ds.Tables.Add(dtStu);

    //添加学生信息记录的列(字段)信息,包括 3个字段:

    //姓名: Name string类型

    //性别: XingBie string类型

    //年龄: Age int类型

    dtStu.Columns.AddRange(new DataColumn[]{

        new DataColumn("Name", Type.GetType("System.String")),

        new DataColumn("XingBie", Type.GetType("System.String")),

        new DataColumn("Age", Type.GetType("System.Int32")),

    });

    // 添加学生信息的行信息,包括:姓名、性别和年龄

    dtStu.Rows.Add("张三 ", " ", 20);

    dtStu.Rows.Add("李四 ", " ", 19);

    dtStu.Rows.Add("王霞 ", " ", 21);

    dtStu.Rows.Add("赵敏 ", " ", 22);

    dtStu.Rows.Add("吴安 ", " ", 18);

    dtStu.Rows.Add("杨花 ", " ", 23);

    return ds;                                                                    //返回数据集

}

static void CreateDataVeiw( )

{

    DataSet ds = BuildDataSet( );                                //获取数据集 ds

    DataTable dt = ds.Tables["Students"];                  //从数据集 ds获取 Students dt

    // DataTable.AsDataView()方法从数据表 dt创建 DataView对象 dvDt

    DataView dvDt = dt.AsDataView( );

    //query1 LINQ查询创建 DataView对象 dvDt,查询它所有的元素

    EnumerableRowCollection<DataRow> query1 =

        from stu in dt.AsEnumerable( )

        select stu;

    DataView dvNml = query1.AsDataView( );                   // 获取查询 query1产生的 DataView

    //query2 LINQ查询创建具有过滤信息的 DataView,查询所有姓“杨”的学生

    EnumerableRowCollection<DataRow> query2 =

        from stu in dt.AsEnumerable( )

        where stu.Field<string>("Name").StartsWith(" ")

        select stu;

    DataView dvFilter = query2.AsDataView( );       // 获取查询 query2产生的 DataView

    //query3 LINQ查询创建具有排序信息的 DataView,将学生按照从小到大的顺序排序

    EnumerableRowCollection<DataRow> query3 =

        from stu in dt.AsEnumerable( )

        orderby stu.Field<int>("Age")

        select stu;

    DataView dvSort = query3.AsDataView( );         //获取查询 query3产生的 DataView

}

很多场合都需要对数据进行排序和过滤操作,在 LINQ to DataSet中有两种方法完成这样的操作,一是通过具有排序和过滤操作得到的 LINQ查询创建 DataView。如示例代码 5-8中的 dvFilter dvSort,前者通过具有 where子句过滤功能的查询 query2创建,后者通过具有 orderby子句排序功能的查询 query3创建, dvFilter dvSort所得到的数据自然是过滤和排序之后的数据集合。另外一种方法是通过 DataView类的 RowFilter Sort属性分别进行过滤和排序操作,这两个属性都是字符串( string)类型。

RowFilter属性接收一个表示过滤条件的字符串,格式为:指定列的名称后跟一个运算符和一个要筛选的值。运算符可以是:等于( =)、大于( >)、小于( <)等。通过设置 RowFilter属性的值,有两种不同的方式清除 DataView上的过滤条件:

q      RowFilter属性设置为 null

q      RowFilter属性设置为一个空字符串。 Sort属性接收一个表示排序信息的字符串,它包含列名,后跟 “ASC”(升序)或 “DESC”(降序)。在默认情况下列按升序排序,多个列可用逗号隔开。清除 DataView中的排序信息也有两种方式:

q      Sort属性设置为 null

q      Sort属性设置为一个空字符串。

示例代码 5-9演示了 RowFilter Sort属性的使用, DataView对象 dvDt是直接从 DataTable对象 dtStu创建而来,包含 dtStu中的所有记录。其中 RowFilter设置为“ Age>20”表示只需要 Age字段大于 20的记录。 Sort设置为“ Age asc, Name desc”表示先按照 Age字段从低到高排序,后按照姓名从高到低排序。

示例代码 5-9

static void UseDataView( )

{

    DataSet ds = BuildDataSet( );                       //获取数据集 ds

    DataTable dt = ds.Tables["Students"];         //从数据集 ds获取 Students dt

    // DataTable.AsDataView()方法从数据表 dt创建 DataView对象 dvDt

    DataView dvDt = dt.AsDataView( );

    // 通过 RowFilter属性设置 DataView过滤信息 ,只需要年龄大于 20岁的学生记录

    dvDt.RowFilter = "Age > 20";

    // 设置 RowFilter null或空字符串,清除过滤信息,二选一

    dvDt.RowFilter = string.Empty;

    dvDt.RowFilter = null;

    //通过 Sort属性设置 DataView排序信息,年龄从小到大排序,姓名从大到小排序

    dvDt.Sort = "Age asc, Name desc";

    // 设置 RowFilter null或空字符串,清除过滤信息,二选一

    dvDt.Sort = string.Empty;

    dvDt.Sort = null;

}

从上面示例代码可以看出,通过 LINQ to DataSet查询和 DataView RowFilter Sort属性都可以进行排序和过滤。但是 LINQ查询提供的排序和过滤功能更加强大,开发人员可以编写自定义的过滤函数、排序方法等。而 RowFilter Sort属性只能是基于字段的表达式,只能实现简单的排序和过滤,由于可以清除,所以更为灵活。

技巧:当 DataView要显示的数据比较复杂,需要进行复杂的过滤或排序操作时,建议使用 LINQ查询来创建 DataView,作为最终或临时视图。然后使用 RowFilter Sort属性进行 DataView灵活的排序和筛选操作。

转载于:https://www.cnblogs.com/deepwishly/archive/2011/05/09/2551160.html

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)