LINQ查询基本操作

1、如何使用LINQ

        LINQ作为一种数据查询编码方式,本身并不是独立的开发语句,也不能进行应用程序开发。在.NET3.5中,可以在C#中集成LINQ查询代码。在任何源代码文件中,要使用LINQ查询功能,必须引用System.Linq命名空间。使用LINQ to XML要引用System.Xml.Linq命名空间,使用LINQ to ADO.NET要引用System.Data.Linq命名空间。

2、LINQ查询表达式

        查询表达式关键字:

    from:指定要查找的数据源以及范围变量,多个from子句则表示从多个数据源中查找数据

    select: 指定查询要返回的目标数据,可以指定任何类型,甚至是匿名类型

    where: 指定元素的筛选条件,多个where子句则表示了并列关系,必须全部都满足才能入选

    orderby: 指定元素的排序字段和排序方式,当有多个排序字段时,有字段顺序确定主次关系,可以指定升序和降序两种排序方式

    group: 指定元素的分组字段

    join: 指定多个数据源的关联方式

2.1 用from子句指定数据源

  每个LINQ查询语句都是以from子句开始,from子句包括以下两个功能:

    ●指定查询将采用的数据源

    ●定义一个本地变量,表示数据源中的单个元素

  如: from localval in datasource

  一般情况下,不用为from子句的localval元素指定数据类型,编译器会根据数据源类型为它分配合适的类型,通常为IEnumerable<T>中的类型T。例如,下面的val会被分配为int类型

    int[] arr =  {1,2,3,4,5};

    var query =

          from val in arr

          select val;

  特殊情况下,开发人员需要为本地变量指定数据类型,比如上面的例子中,如果希望将arr中的元素作为object类型进行处理,而不是int类型,需要指定val2为object类型,因为arr中元素是int类型,数据object类型的子类型,所以可以直接转换,如下:

    int[] arr =  {1,2,3,4,5};

    var query =

          from object val2 in arr

          select val2;

  值得注意的是,编译器并不会检查本地变量的具体类型,所以即使指定类型不正确,编译器也不会报错,但是在下面使用该查询时,会在运行时进行类型检查,从而产生异常。

2.2 用select子句指定目标数据

  LINQ查询表达式必须以select或者group子句结束。select子句中要选择的目标数据不仅可以是数据源中的元素,还可以是该元素的不同操作结果,包括属性、方法、运算,如下面两个例子:   

var query2 =
                from val in arr
                select val.Name;
            foreach(string item in query2)
            {
                Console.Write("{0},   ",item);
            }
            Console.WriteLine();
            var query3 =
                from val in arr
                select val.Name.Length;
            foreach (int item in query3)
            {
                Console.Write("{0},   ", item);
            }

  在某些特殊的场合,往往查询结果只是临时使用一下,而且查询结果的数据包括很多字段,并非简单的一个属性、方法返回值等。在LINQ中,可以在select子句中使用匿名类型来解决这个问题。如query4中的select子句通过匿名类型定义返回结果,因为编码无法使用匿名类型,所以在foreach只能通过var(可变类型)关键字让编译器自动判断查询中元素类型。

            var query4 =
                from val in arr
                select new { val.Name, val.Age,  NameLen = val.Name.Length};
            foreach(var item2 in query4)
            {
                Console.WriteLine(item2);
            }            

2.3 用where子句指定筛选条件

   作用:指定查询的过滤条件

  格式:where Expression

  int[] ary = {1,2,3,4,5,6,7,8,9,10,11,12};

  var query =

        from val in ary

        where (val > 5) && (val < 10)

        select val;

2.4 用orderby子句进行排序

   作用:对查询结果进行排序

  格式: orderby element [sortType]

  其中,sortType是可选参数,表示排序类型,包括升序(ascending)和降序(descending)两个参数,默认为升序。

            int[] ary = { 9,54,32,1,67,43,0,9,7,4,9,2,23,66,61};
            var query5 =
                from val in ary
                orderby val
                select val;
            foreach(var item in query5)
            {
                Console.Write("{0}   ",item);
            }
            Console.WriteLine();
            Console.WriteLine();
            var query6 =
                from val in ary
                orderby val descending
                select val;
            foreach (var item in query6)
            {
                Console.Write("{0}   ", item);
            }

结果是:

   

  在LINQ中,orderby可以同时指定多个排序元素,也可以为每个排序元素指定独立的排序方式。orderby后的第一个排序元素为主要排序,第二个为次要排序,依次类推。如下面例子,先按照姓名字符数量进行升序排列,再按照年龄降序排列。 

            var query7 =
                from val in arr
                orderby val.Name.Length ascending, val.Age descending
                select val;
            foreach(var item in query7)
            {
                Console.WriteLine(item);
            }

结果:

2.5 用group子句进行分组

   作用:对数据进行分组

  格式:group element by key

            var query8 =
                from val in arr
                group val by val.Xingbie;
            foreach(var grp in query8)
            {
                Console.WriteLine(grp.Key);
                foreach(var st in grp)
                {
                    Console.WriteLine("	{0}",st);
                }
            }

  有时候需要对分组结果进行排序、在查询等操作,这时候就需要使用into 关键字将group查询结果存在一个临时变量中,并且必须使用新的select或者group子句对其进行查询,也可使用orderby进行排序,where进行过滤等等,into的语句格式如下

    group element by key into tmpgrp

  其中,tmpgrp就是临时变量,用来临时保存group产生的结果,提供后面的查询操作。

            var query9 =
                from val in arr
                group val by val.Age into stgrp
                orderby stgrp.Key descending
                select stgrp;
            foreach(var st in query9)
            {
                Console.WriteLine("{0}岁的学生:",st.Key);
                foreach(var stu in st)
                {
                    Console.WriteLine("	{0}",stu);
                }
            }

2.6 用join子句进行关联

   join子句实现联接操作,将来自不同源序列,并且在对象模型中没有直接关系的元素相关联,唯一的要求就是每个源中的元素需要共享某个可以进行比较,以判断是否相等的值。

  join子句可以实现3种类型的联接: 内部联接、分组联接、左外部联接。

2.6.1 用join子句进行内部联接

  格式: join element in datasource on exp1 equals exp2

  datasource 表示数据源,它是联接要使用的第二个数据集,element 表示存储datasource中元素的本地变量,exp1 和 exp2 表示两个表达式,它们具有相同的数据类型,可以用equals进行比较,如果exp1 和 exp2 相等,则当前元素将添加到查询结果中。

            int[] intarray1 = { 5,15,25,30,33,40};
            int[] intarray2 = { 10,20,30,40,50,60,70,80,90,100};
            var query12 =
                from val1 in intarray1
                join val2 in intarray2 on val1 % 5 equals val2 % 15
                select new { VAL1 = val1,VAL2 = val2};
            foreach(var val in query12)
            {
                Console.WriteLine(val);
            }    

2.6.2 用join子句进行分组联接

  格式:join element in datasource on exp1 equals exp2 into grpname

  into 关键字表示将这些数据分组并保存到grpname中,grpname是保存一组数据的集合。

  分组联接产生分层的数据结果,它将第一个集合中的每一个元素与第二个集合中的一组相关元素进行配对。值得注意的是,即使第一个集合中的元素在第二个集合中没有配对的元素,也会为它产生一个空的分组对象。

            var query13 =
                from val1 in intarray1
                join val2 in intarray2 on val1 % 5 equals val2 % 15 into grpName
                select new { VAL1 = val1, VAL2 = grpName};
            foreach(var val in query13)
            {
                Console.Write("{0}:  ",val.VAL1);
                foreach(var obj in val.VAL2)
                {
                    Console.Write("{0}  ",obj);
                }
                Console.WriteLine();
            }    

请比较query12 和 query13 的运行结果的区别。

2.6.3 用join子句进行左外部联接

   左外部联接返回第一个集合元素的所有元素,无论它是否在第二个集合中有相关元素。在LINQ中,通过对分组联接的结果调用DefaultEmpty()来执行左外部联接。

            var query14 =
                from val1 in intarray1
                join val2 in intarray2 on val1 % 5 equals val2 % 15 into grpName
                from grp in grpName.DefaultIfEmpty()
                select new { VAL1 = val1 , VAL2 = grp};
            foreach(var val in query14)
            {
                Console.WriteLine(val);
            }

2.7 使用let子句扩展范围变量

  用于创建查询自身的范围变量

string[] strings ={
                 "I am a new Student.",
                 "You are a talent"
                 };

var query = from sentences in strings
            let words = sentences.Split(' ')
            from word in words
            let w = word.ToLower()
            where w[0] == 'a' || w[0] == 'e' || w[0] == 'i' || w[0] == 'o' || 
                     w[0] == 'u'
            select word;

foreach (var word in query)
{
    Console.Write(word + ",");
}

  需求:将字符串数组中的两句英文语句中所有的元音字母打头的单词输出到控制台

  分析:首先遍历字符串数组中的每个字符串,用let子句创建查询自身的范围变量words,并调用Split(' ')方法,将每个字符串中以空格分割为单词存入words变量中,然后再次使用let子句创建查询自身的范围变量word,并调用ToLower()方法,将每个单词都变为小写,最后筛选出首字母为元音的单词进行返回。

PS:本文完整代码来自:http://www.cnblogs.com/crandy/p/4546126.html

原文地址:https://www.cnblogs.com/crandy/p/4546110.html