LINQ 笔记 - 语法与关键字
2008-02-27 21:27 Animax! 阅读(651) 评论(3) 编辑 收藏 举报
查询操作的三个部分:
所有 LINQ 查询操作都由以下三个不同的操作组成:
1.获取数据源。
2.创建查询。
3.执行查询。
这里的第二步仅仅是创建查询条件(一个匿名函数),查询条件里面是不包含查询结果的。
真正的执行是在第三步里,这个感念,被称为“延迟执行”。
要LINQ查询强制立即执行可以使用ToArray和ToList方法。
如果要对数据源执行聚合函数的查询如:Count、Max、Average 和 First ,也会导致此LINQ执行强制立即查询。
对于一个LINQ查询,form关键字是必然存在的,select或group关键字也是必然存在的,
因为它必须是以select关键字开头和以select或group关键字结尾。
最简单的就如上面代码:
在某些情况下,源序列中的每个元素本身可能是序列,也可能包含序列,这时就需要用到 复合 from 子句 。
复合 from 子句用于访问单个数据源中的内部集合。
不过,查询还可以包含多个可从独立数据源生成补充查询的 from 子句。
这段代码将会输出upperCase和lowerCase的笛卡尔乘积。
static void Main()
{
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
// 搜索是偶数并且小于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子句由两个部分组成,group关键字之后的对象指名返回列表对象的类型,by关键字之后的是如果分组的条件。
分组条件的标识将会记录在返回对象列表的key属性中。
示例:
在 group 或 select 子句中使用新标识符的用法有时称为“延续”。
唯一的要求是每个源中的元素需要共享某个可以进行比较以判断是否相等的值。
JOIN 链接一般能分为3种:
内部连接:
这里出现了一个新的运算符: equals
这个运算符是LINQ独有的,执行等同链接,equals 与 == 运算符之间的一个重要的区别。
对于 equals,左键使用外部源序列,而右键使用内部源序列。外部源仅在 equals 的左侧位于范围内,
而内部源序列仅在其右侧位于范围内。
分组联接:
含有 into 表达式的 join 子句称为分组联接。
左外部联接:
在左外部联接中,将返回左侧源序列中的所有元素,即使它们在右侧序列中没有匹配的元素也是如此。
所有 LINQ 查询操作都由以下三个不同的操作组成:
1.获取数据源。
2.创建查询。
3.执行查询。
static void Main()
{
// 1. 数据源
int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };
// 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);
}
}
{
// 1. 数据源
int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };
// 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查询强制立即执行可以使用ToArray和ToList方法。
var numQuery =
(from num in numbers
where (num % 2) == 0
select num).ToList<int>();
(from num in numbers
where (num % 2) == 0
select num).ToList<int>();
如果要对数据源执行聚合函数的查询如:Count、Max、Average 和 First ,也会导致此LINQ执行强制立即查询。
LINQ 的关键字:
对于一个LINQ查询,form关键字是必然存在的,select或group关键字也是必然存在的,
因为它必须是以select关键字开头和以select或group关键字结尾。
FORM 子句:
form子句需要指定查询或子查询的数据源和一个本地范围变量。最简单的就如上面代码:
var numQuery =
from num in numbers
select num;
from num in numbers
select num;
在某些情况下,源序列中的每个元素本身可能是序列,也可能包含序列,这时就需要用到 复合 from 子句 。
public class Student
{
public string LastName { get; set; }
public List<int> Scores { get; set; }
}
static void Main()
{
// 建立数据源
List<Student> students = new List<Student>
{
new Student {LastName="Omelchenko", Scores= new List<int> {97, 72, 81, 60}},
new Student {LastName="O'Donnell", Scores= new List<int> {75, 84, 91, 39}},
new Student {LastName="Mortensen", Scores= new List<int> {88, 94, 65, 85}},
new Student {LastName="Garcia", Scores= new List<int> {97, 89, 85, 82}},
new Student {LastName="Beebe", Scores= new List<int> {35, 72, 91, 70}}
};
// 使用复合 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
*/
}
{
public string LastName { get; set; }
public List<int> Scores { get; set; }
}
static void Main()
{
// 建立数据源
List<Student> students = new List<Student>
{
new Student {LastName="Omelchenko", Scores= new List<int> {97, 72, 81, 60}},
new Student {LastName="O'Donnell", Scores= new List<int> {75, 84, 91, 39}},
new Student {LastName="Mortensen", Scores= new List<int> {88, 94, 65, 85}},
new Student {LastName="Garcia", Scores= new List<int> {97, 89, 85, 82}},
new Student {LastName="Beebe", Scores= new List<int> {35, 72, 91, 70}}
};
// 使用复合 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
*/
}
{
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 = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
// 搜索是偶数并且小于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
*/
{
// 获取数据源
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 为上下文关键字创建一个临时标识符,以便将 group、join 或 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 个。
*/
{
// 创建数据源
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
*/
{
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种:
数据源
内部连接:
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 };
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 };
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 };
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;
{
"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;