代码改变世界

LINQ 笔记 - 语法与关键字

2008-02-27 21:27  Animax!  阅读(651)  评论(3编辑  收藏  举报
查询操作的三个部分:

所有 LINQ 查询操作都由以下三个不同的操作组成:
1.获取数据源。
2.创建查询
3.执行查询


    static void Main()
    
{
        
// 1. 数据源
        int[] numbers = new int[70123456 };

        
// 2. 创建查询
        
// 这里numQuery变量的类型为IEnumerable<int>
        var numQuery =
            from num 
in numbers
            
where (num % 2== 0
            select num;

        
// 3. 执行查询
        foreach (int num in numQuery)
        
{
            Console.Write(
"{0,1} ", num);
        }

    }


这里的第二步仅仅是创建查询条件(一个匿名函数),查询条件里面是不包含查询结果的。
真正的执行是在第三步里,这个感念,被称为“延迟执行”。

要LINQ查询强制立即执行可以使用ToArrayToList方法。

    var numQuery =
        (from num 
in numbers
         
where (num % 2== 0
         select num).ToList
<int>();

如果要对数据源执行聚合函数的查询如:CountMaxAverageFirst ,也会导致此LINQ执行强制立即查询。



LINQ 的关键字:


form: 指定数据源和范围变量(类似于迭代变量)。
where:根据一个或多个由逻辑“与”和逻辑“或”运算符(&& 或 ||)分隔的布尔表达式筛选源元素。
select:指定当执行查询时返回的序列中的元素将具有的类型和形式。
group:按照指定的键值对查询结果进行分组。
into:提供一个标识符,它可以充当对 join、group 或 select 子句的结果的引用。
orderby:基于元素类型的默认比较器按升序或降序对查询结果进行排序。
join:基于两个指定匹配条件之间的相等比较来联接两个数据源。
let:引入一个用于存储查询表达式中的子表达式结果的范围变量。

  对于一个LINQ查询,form关键字是必然存在的,select或group关键字也是必然存在的,
因为它必须是以select关键字开头和以select或group关键字结尾。


FORM 子句:

form子句需要指定查询或子查询的数据源和一个本地范围变量。

最简单的就如上面代码:

    var numQuery =
        from num 
in numbers
        select num;


在某些情况下,源序列中的每个元素本身可能是序列,也可能包含序列,这时就需要用到 复合 from 子句

        public class Student
        
{
            
public string LastName getset; }
            
public List<int> Scores getset; }
        }


        
static void Main()
        
{

            
// 建立数据源
            List<Student> students = new List<Student>
            
{
               
new Student {LastName="Omelchenko", Scores= new List<int> {97728160}},
               
new Student {LastName="O'Donnell", Scores= new List<int> {75849139}},
               
new Student {LastName="Mortensen", Scores= new List<int> {88946585}},
               
new Student {LastName="Garcia", Scores= new List<int> {97898582}},
               
new Student {LastName="Beebe", Scores= new List<int> {35729170}}
            }
;

            
// 使用复合 from 子句循环搜索出每个学生的各个成绩
            var scoreQuery =
                 from student 
in students
                 from score 
in student.Scores
                 
where score > 90
                 select 
new { Last = student.LastName, score };  // 匿名类型
                 
            
/*
            scoreQuery:
            Omelchenko Score: 97
            O'Donnell Score: 91
            Mortensen Score: 94
            Garcia Score: 97
            Beebe Score: 91
            
*/

            
         }


复合 from 子句用于访问单个数据源中的内部集合。
不过,查询还可以包含多个可从独立数据源生成补充查询的 from 子句。

        static void Main()
        
{

            
char[] upperCase = 'A''B''C' };
            
char[] lowerCase = 'x''y''z' };

            var Query 
=
                from upper 
in upperCase
                from lower 
in lowerCase
                select 
new { upper, lower };

            
foreach (var value in Query)
                Console.WriteLine(
"{0} , {1}", value.upper, value.lower);

            Console.ReadKey();
           
            
/*
            Query:
            A , x
            A , y
            A , z
            B , x
            B , y
            B , z
            C , x
            C , y
            C , z
            
*/


        }



这段代码将会输出upperCase和lowerCase的笛卡尔乘积。


WHERE 子句:

where子句用于指定将在查询表达式中返回数据源中的哪些元素。


        
static void Main()
        
{
            
int[] numbers = 5413986720 };
           
            
// 搜索是偶数并且小于7的元素
            var queryEvenNums =
                    from num 
in numbers
                    
where IsEven(num) && num< 7
                    select num;

            
// 执行查询
            foreach (var s in queryEvenNums)
            
{
                Console.Write(s.ToString() 
+ " ");
            }


            Console.ReadKey();
        }


        
// 判断是否偶数
        static bool IsEven(int i)
        
{
            
return i % 2 == 0;
        }

       
        
/*
        queryEvenNums:
        4 6 2 0
        
*/


GROUP 子句

group子句返回一个 IGrouping(TKey, TElement) 对象序列,这些对象包含零个或更多个与该组的键值匹配的项。
也就是说,group子句出来的对象其实就是一个列表,

group子句由两个部分组成,group关键字之后的对象指名返回列表对象的类型,by关键字之后的是如果分组的条件。
分组条件的标识将会记录在返回对象列表的key属性中。

示例:
   
数据源

    static void Main()
    
{
        
// 获取数据源
        List<Student> students = GetStudents();

        
// 创建查询条件
        
// booleanGroupQuery 变量的类型是 IEnumerable<IGrouping<bool, Student>>
        var booleanGroupQuery =
            from student 
in students
            group student by student.Scores.Average() 
>= 80;
        
// 返回 student 类型的列表
        
// 分组的条件是bool类型,是按照学生平均分是否大于80来进行分组的

        
// 使用嵌套循环来取出值
        foreach (var studentGroup in booleanGroupQuery)
        
{
            Console.WriteLine(studentGroup.Key 
== true ? "高分" : "低分"); //显示分组信息

            
foreach (var student in studentGroup)
            
{
                Console.WriteLine(
"   {0}. {1}:{2}", student.Last, student.First, student.Scores.Average());
            }

        }


        Console.ReadKey();
    }

   
    
/*
    低分
       Omelchenko. Svetlana:77.5
       O'Donnell. Claire:72.25
       Garcia. Cesar:75.5
    高分
       Mortensen. Sven:93.5
       Garcia. Debra:88.25
    
*/


INTO 子句:

into 为上下文关键字创建一个临时标识符,以便将 groupjoin select 子句的结果存储到新的标识符中。
在 group 或 select 子句中使用新标识符的用法有时称为“延续”。

    static void Main()
    
{
        
// 创建数据源
        string[] words = "apples""blueberries""oranges""bananas""apricots" };
       
        var wordGroups 
=
            from w 
in words
            group w by w[
0] into fruitGroup      // 以字符串的第一个字符来进行分组,并把结果列表送入fruitGroup。
            where fruitGroup.Count() >= 2
            select 
new { FirstLetter = fruitGroup.Key, Words = fruitGroup.Count() };

        
foreach (var item in wordGroups)
        
{
            Console.WriteLine(
" '{0}' 有 {1} 个。", item.FirstLetter, item.Words);
        }


        Console.ReadKey();
    }


    
/*
     'a' 有 2 个。
     'b' 有 2 个。
    
*/



ORDERBY 子句:

orderby子句可使返回的序列或子序列(组)按升序或降序排序。
 
    static void Main()
    
{
        
int[] number = {2,1,0,7,3,6,5,4};

        var Ascending 
=
            from num 
in number
            orderby num 
// 默认使用 ascending
            select num;

        var Descendin 
=
            from num 
in number
            orderby num descending
            select num;

        Console.WriteLine(
"升序:");
        
foreach (var item in Ascending)
        
{
            Console.Write(item 
+ " " );
        }


        Console.WriteLine();
        Console.WriteLine(
"降序:");
        
foreach (var item in Descendin)
        
{
            Console.Write(item 
+ " ");
        }


        Console.ReadKey();
    }

   
    
/*
    升序:
    0 1 2 3 4 5 6 7
    降序:
    7 6 5 4 3 2 1 0
    
*/



JOIN 子句:

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

JOIN 链接一般能分为3种:
  1.内部联接
  2.分组联接
  3.左外部联接


数据源


内部连接:
    var innerJoinQuery =
        from category 
in categories
        join prod 
in products on category.ID equals prod.CategoryID   //这里 category.ID 与 prod.CategoryID 有关联
        select new { ProductName = prod.Name, Category = category.Name };


  这里出现了一个新的运算符: equals
  这个运算符是LINQ独有的,执行等同链接,equals 与 == 运算符之间的一个重要的区别。
  对于 equals,左键使用外部源序列,而右键使用内部源序列。外部源仅在 equals 的左侧位于范围内,
而内部源序列仅在其右侧位于范围内。


分组联接:
含有 into 表达式的 join 子句称为分组联接。
    var innerGroupJoinQuery =
        from category 
in categories
        join prod 
in products on category.ID equals prod.CategoryID into prodGroup
        select 
new { CategoryName = category.Name, Products = prodGroup };



左外部联接:
在左外部联接中,将返回左侧源序列中的所有元素,即使它们在右侧序列中没有匹配的元素也是如此。

    var leftOuterJoinQuery =
        from category 
in categories
        join prod 
in products on category.ID equals prod.CategoryID into prodGroup
        from item 
in prodGroup.DefaultIfEmpty(new Product{Name = String.Empty, CategoryID = 0})
            select 
new { CatName = category.Name, ProdName = item.Name };


LET 子句:

在查询表达式中,let子句 存储子表达式的结果有时很有用,这样可以在随后的子句中使用。

    string[] strings =
        
{
            
"A penny saved is a penny earned.",
            
"The early bird catches the worm.",
            
"The pen is mightier than the sword."
        }
;

    
// 这里words是分解
    
// w是word的小写版本
    var earlyBirdQuery =
        from sentence 
in strings
        let words 
= sentence.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;