前言
上篇文章我们说过,LINQ语法中,包含了查询表达式语法和标准查询方法语法,下面我们举例来说明, 如何分别用上述两种不同方式编写常用查询语句。
下表列出包含等效查询表达式子句的标准查询运算符。
方法 | C# 查询表达式语法 |
---|---|
Cast | 使用显式类型化范围变量,例如: from int i in numbers (有关详细信息,请参阅 from 子句。) |
GroupBy | group … by - 或 - group … by … into … (有关详细信息,请参阅 group 子句。) |
GroupJoin(IEnumerable, IEnumerable, Func, Func, Func,TResult>) | join … in … on … equals … into … (有关详细信息,请参阅 join 子句。) |
Join(IEnumerable, IEnumerable, Func, Func, Func) | join … in … on … equals … (有关详细信息,请参阅 join 子句。) |
OrderBy(IEnumerable, Func) | orderby (有关详细信息,请参阅 orderby 子句。) |
OrderByDescending(IEnumerable, Func) | orderby … descending (有关详细信息,请参阅 orderby 子句。) |
Select | select (有关详细信息,请参阅 let 子句。) |
SelectMany | 多个 from 子句。 (有关详细信息,请参阅 from 子句。) |
ThenBy(IOrderedEnumerable, Func) | orderby …, … (有关详细信息,请参阅 orderby 子句。) |
ThenByDescending(IOrderedEnumerable, Func) | orderby …, … descending (有关详细信息,请参阅 orderby 子句。) |
Where | where (有关详细信息,请参阅 where 子句。) |
下面是代码示例:
Where子句
where
子句是一种筛选机制。 除了不能是第一个或最后一个子句外,它几乎可以放在查询表达式中的任何位置。 where
子句可以出现在 group 子句的前面或后面,具体取决于时必须在对源元素进行分组之前还是之后来筛选源元素。
在编译时,where
关键字将转换为对 Where 标准查询运算符方法的调用
在下面的示例中,where
子句筛选出除小于五的数字外的所有数字。 如果删除 where
子句,则会返回数据源中的所有数字。 表达式 num < 5
是应用于每个元素的谓词。
class WhereSample
{
static void Main()
{
// 单数据源. Arrays support IEnumerable<T>.
int[] numbers = {
5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
// 查询语句.
var queryLowNums =
from num in numbers
where num < 5
select num;
// 方法语句
var query = numbers.Where(num => num<5)
// 执行查询.
foreach (var s in queryLowNums)
{
Console.Write(s.ToString() + " ");
}
}
}
//Output: 4 1 3 2 0
from 子句
查询表达式必须以 from
子句开头。 此外,查询表达式可包含也以 from
子句开头的子查询。 from
子句指定下列各项:
- 将在其上运行查询或子查询的数据源。
- 表示源序列中每个元素的本地范围变量。
范围变量和数据源已强类型化。 from
子句中引用的数据源必须具有 IEnumerable、IEnumerable 类型之一,或 IQueryable 等派生类型。
在编译时,单个from
关键字将转换为对 Select 标准查询运算符方法的调用,而多个from
关键字将转化为对SelectMany标准查询运算符方法的调用
下面是一个示例:
class TestSelectMany2
{
// 数据源类型.
public class Student
{
public string LastName {
get; set; }
public List<int> Scores {
get; set; }
}
public static void Test()
{
// 列表中的每个元素都包含一个内部的分数序列。
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};
//使用selectManay方法查询
var scoreQuery2 = students
.SelectMany(student => student.Scores, (student, score) => new {
student, score})
.Where(st => st.score > 90)
.Select(st => new {
Last = st.student.LastName, st.score});
// 执行查询.
Console.WriteLine("scoreQuery:");
foreach (var student in scoreQuery)
{
Console.WriteLine("{0} Score: {1}", student.Last, student.score);
}
// 在debug模式下保持控制台开启状态
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/*
scoreQuery:
Omelchenko Score: 97
O'Donnell Score: 91
Mortensen Score: 94
Garcia Score: 97
Beebe Score: 91
*/
Group子句
group
子句返回一个 Grouping<TKey,TElement> 对象序列,这些对象包含零个或更多与该组的键值匹配的项。 例如,可以按照每个字符串中的第一个字母对字符串序列进行分组。 在这种情况下,第一个字母就是键,类型为 char,并且存储在每个 IGrouping 对象的 Key
属性中。 编译器可推断键的类型。
在编译时,group
子句转换为对 GroupBy 方法的调用。
下面看示例
class GroupSample1
{
// 数据源类型
public class Student
{
public string First {
get; set; }
public string Last {
get; set; }
public int ID {
get; set; }
public List<int> Scores;
}
//数据源
public static List<Student> GetStudents()
{
// Use a collection initializer to create the data source. Note that each element
// in the list contains an inner sequence of scores.
List<Student> students = new List<Student>
{
new Student {
First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {
97, 72, 81, 60}},
new Student {
First="Claire", Last="O'Donnell", ID=112, Scores= new List<int> {
75, 84, 91, 39}},
new Student {
First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {
99, 89, 91, 95}},
new Student {
First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {
72, 81, 65, 84}},
new Student {
First="Debra", Last="Garcia", ID=115, Scores= new List<int> {
97, 89, 85, 82}}
};
return students;
}
public static void Test()
{
// Obtain the data source.
List<Student> students = GetStudents();
// 查询语句
var booleanGroupQuery1 =
from student in students
group student by student.Scores.Average() >= 80; //pass or fail!
//标准方法语句
var booleanGroupQuery2 = students.GroupBy(student => student.Scores.Average() >= 80);
// 执行查询,并访问每个group中的items
foreach (var studentGroup in booleanGroupQuery2)
{
Console.WriteLine(studentGroup.Key == true ? "High averages" : "Low averages");
foreach (var student in studentGroup)
{
Console.WriteLine(" {0}, {1}:{2}", student.Last, student.First, student.Scores.Average());
}
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
Low averages
Omelchenko, Svetlana:77.5
O'Donnell, Claire:72.25
Garcia, Cesar:75.5
High averages
Mortensen, Sven:93.5
Garcia, Debra:88.25
*/
OrderBy和ThenBy子句
orderby
子句可导致返回的序列或子序列(组)以升序或降序排序。 若要执行一个或多个次级排序操作,可以指定多个键。 元素类型的默认比较器执行排序。 默认排序顺序为升序。 还可以指定自定义比较器。 但是,只适用于使用基于方法的语法。 有关详细信息,请参阅对数据进行排序。
编译时,orderby
子句将转换为对 OrderBy 方法的调用。 orderby
子句中的多个关键值将转换为 ThenBy 方法调用。
同理orderby … descending
是默认降序排序,编译时,自动转化为OrderByDescending方法和ThenByDescending方法的调用。
下面是示例:
class OrderbySample1
{
static void Test()
{
// 数据源
string[] fruits = {
"cherry", "apple", "blueberry" };
// 查询语法,升序排序
IEnumerable<string> sortAscendingQuery =
from fruit in fruits
orderby fruit //"ascending" is default,默认是升序
select fruit;
//方法语法
IEnumerable<string> sortAscendingQuery2 = fruits
.OrderBy(fruit => fruit);
// 查询语法,降序排序
IEnumerable<string> sortDescendingQuery =
from w in fruits
orderby w descending
select w;
// 方法语法1,降序排序
IEnumerable<string> sortDescendingQuery2 = fruits
.OrderByDescending(fruit => fruit);
// 方法语法2,降序排序
IEnumerable<string> sortDescendingQuery3 = fruits
.OrderBy(fruit => fruit)
.ThenByDescending(fruit => fruit);
// Execute the query.
Console.WriteLine("Ascending:");
foreach (string s in sortAscendingQuery)
{
Console.WriteLine(s);
}
// Execute the query.
Console.WriteLine(Environment.NewLine + "Descending:");
foreach (string s in sortDescendingQuery)
{
Console.WriteLine(s);
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
Ascending:
apple
blueberry
cherry
Descending:
cherry
blueberry
apple
*/
Join子句
join
子句可用于将来自不同源序列并且在对象模型中没有直接关系的元素相关联。 唯一的要求是每个源中的元素需要共享某个可以进行比较以判断是否相等的值。 例如,食品经销商可能拥有某种产品的供应商列表以及买主列表。 例如,可以使用 join
子句创建该产品同一指定地区供应商和买主的列表。
join
子句将 2 个源序列作为输入。 每个序列中的元素都必须是可以与其他序列中的相应属性进行比较的属性,或者包含一个这样的属性。 join
子句使用特殊 equals
关键字比较指定的键是否相等。 join
子句执行的所有联接都是同等联接。 join
子句的输出形式取决于执行的联接的具体类型。 以下是 3 种最常见的联接类型:
- 内部联接
- 分组联接
- 左外部联接
下面看示例:
class JoinSample1
{
#region Data 数据源区域
class Product
{
public string Name {
get; set; }
public int CategoryID {
get; set; }
}
class Category
{
public string Name {
get; set; }
public int ID {
get; set; }
}
// 数据源1
List<Category> categories = new List<Category>()
{
new Category {
Name="Beverages", ID=001},
new Category {
Name="Condiments", ID=002},
new Category {
Name="Vegetables", ID=003},
new Category {
Name="Grains", ID=004},
new Category {
Name="Fruit", ID=005}
};
// 数据源2
List<Product> products = new List<Product>()
{
new Product {
Name="Cola", CategoryID=001},
new Product {
Name="Tea", CategoryID=001},
new Product {
Name="Mustard", CategoryID=002},
new Product {
Name="Pickles", CategoryID=002},
new Product {
Name="Carrots", CategoryID=003},
new Product {
Name="Bok Choy", CategoryID=003},
new Product {
Name="Peaches", CategoryID=005},
new Product {
Name="Melons", CategoryID=005},
};
#endregion
public static void Test()
{
JoinSample1 app = new JoinSample1();
app.InnerJoin();
app.GroupJoin();
app.GroupInnerJoin();
app.GroupJoin3();
app.LeftOuterJoin();
app.LeftOuterJoin2();
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
void InnerJoin()
{
//查找所有category.id和prod》categoryId相同的所有数据
// 查询语法
var innerJoinQuery =
from category in categories
join prod in products on category.ID equals prod.CategoryID
select new {
Category = category.ID, Product = prod.Name };
//标准方法语法
var innerJoinQuery2 = categories.Join(products,
category => category.ID,
prod => prod.CategoryID,
(category, prod) => new {
Category = category.ID, Product = prod.Name});
Console.WriteLine("InnerJoin:");
// Execute the query. Access results
// with a simple foreach statement.
foreach (var item in innerJoinQuery)
{
Console.WriteLine("{0,-10}{1}", item.Product, item.Category);
}
Console.WriteLine("InnerJoin: {0} items in 1 group.", innerJoinQuery.Count());
Console.WriteLine(System.Environment.NewLine);
}
void GroupJoin()
{
// 这是一个join查询,添加与category的id相同的categoryid的所有prod为一组,随后输出所有到prodGroup
//查询语法
var groupJoinQuery =
from category in categories
join prod in products on category.ID equals prod.CategoryID into prodGroup
select prodGroup;
//标准语法
var groupJoinQuery2 = categories.GroupJoin(products,
category=>category.ID,
prod=>prod.CategoryID,
(category, prodGroup)=> prodGroup);
// Store the count of total items (for demonstration only).
int totalItems = 0;
Console.WriteLine("Simple GroupJoin:");
// A nested foreach statement is required to access group items.
foreach (var prodGrouping in groupJoinQuery2)
{
Console.WriteLine("Group:");
foreach (var item in prodGrouping)
{
totalItems++;
Console.WriteLine(" {0,-10}{1}", item.Name, item.CategoryID);
}
}
Console.WriteLine("Unshaped GroupJoin: {0} items in {1} unnamed groups", totalItems, groupJoinQuery.Count());
Console.WriteLine(System.Environment.NewLine);
}
void GroupInnerJoin()
{
// 这是一个join查询,添加与category的id相同的categoryid的所有prod为一组,随后输出所有到prodGroup
// 最后对prodGroup的所有prod数据,按Name排序,输出prod序列
//查询语法
var groupJoinQuery =
from category in categories
orderby category.ID
join prod in products on category.ID equals prod.CategoryID into prodGroup
select new
{
Category = category.Name,
Products = from prod2 in prodGroup
orderby prod2.Name
select prod2
};
//标准方法语法
var groupJoinQuery2 = categories.
OrderBy(category=>category.ID).
GroupJoin(products,
category=>category.ID,
prod=>prod.CategoryID,
(category, prodGroup)=>new
{
Category = category.Name,
Products = prodGroup.OrderBy(prod2=>prod2.Name)
});
//Console.WriteLine("GroupInnerJoin:");
int totalItems = 0;
Console.WriteLine("GroupInnerJoin:");
foreach (var productGroup in groupJoinQuery)
{
Console.WriteLine(productGroup.Category);
foreach (var prodItem in productGroup.Products)
{
totalItems++;
Console.WriteLine(" {0,-10} {1}", prodItem.Name, prodItem.CategoryID);
}
}
Console.WriteLine("GroupInnerJoin: {0} items in {1} named groups", totalItems, groupJoinQuery2.Count());
Console.WriteLine(System.Environment.NewLine);
}
void GroupJoin3()
{
// 这是一个join查询,添加与category的id相同的categoryid的所有prod为一组,随后输出所有到prodGroup
// 最后对prodGroup的所有prod数据,按CategoryID排序,输出prod序列
//查询语法
var groupJoinQuery =
from category in categories
join product in products on category.ID equals product.CategoryID into prodGroup
from prod in prodGroup
orderby prod.CategoryID
select new {
Category = prod.CategoryID, ProductName = prod.Name };
//标准方法语法
var groupJoinQuery2 = categories.GroupJoin(products,
category => category.ID,
product => product.CategoryID,
(category, prodGroup) => prodGroup.OrderBy(prod=>prod.CategoryID)
).SelectMany(prod=>prod, (prods, prod)=> new{
Category = prod.CategoryID, ProductName = prod.Name});
//Console.WriteLine("GroupInnerJoin:");
int totalItems = 0;
Console.WriteLine("GroupJoin3:");
foreach (var item in groupJoinQuery2)
{
totalItems++;
Console.WriteLine(" {0}:{1}", item.ProductName, item.Category);
}
Console.WriteLine("GroupJoin3: {0} items in 1 group", totalItems);
Console.WriteLine(System.Environment.NewLine);
}
void LeftOuterJoin()
{
// 这是一个join查询,添加与category的id相同的categoryid的所有prod为一组,随后输出所有到prodGroup
// 最后查找空的prodGroup组,添加一个默认的Product数据
// 查询语法
var leftOuterQuery =
from category in categories
join prod in products on category.ID equals prod.CategoryID into prodGroup
select prodGroup.DefaultIfEmpty(new Product() {
Name = "Nothing!", CategoryID = category.ID });
//标准方法语法
var leftOuterQuery2 = categories.GroupJoin(products,
category => category.ID,
prod => prod.CategoryID,
(category, prodGroup) => prodGroup.DefaultIfEmpty(new Product(){
Name = "Nothing!",CategoryID = category.ID}));
// Store the count of total items (for demonstration only).
int totalItems = 0;
Console.WriteLine("Left Outer Join:");
// A nested foreach statement is required to access group items
foreach (var prodGrouping in leftOuterQuery2)
{
Console.WriteLine("Group:");
foreach (var item in prodGrouping)
{
totalItems++;
Console.WriteLine(" {0,-10}{1}", item.Name, item.CategoryID);
}
}
Console.WriteLine("LeftOuterJoin: {0} items in {1} groups", totalItems, leftOuterQuery.Count());
Console.WriteLine(System.Environment.NewLine);
}
void LeftOuterJoin2()
{
// 这是一个join查询,添加与category的id相同的categoryid的所有prod为一组,随后输出所有到prodGroup
// 最后查找空的prodGroup组,添加一个Default数据,默认为null,最后自己可以再根据判空处理
// 查询语法
var leftOuterQuery =
from category in categories
join prod in products on category.ID equals prod.CategoryID into prodGroup
from item in prodGroup.DefaultIfEmpty()
select new {
Name = item == null ? "Nothing!" : item.Name, CategoryID = category.ID };
//标准方法语法
var leftOuterQuery2 = categories.GroupJoin(products,
category=>category.ID,
prod=>prod.CategoryID,
(category, prodGroup)=>prodGroup.DefaultIfEmpty(new Product(){
Name="Nothing",CategoryID= category.ID})
).SelectMany(prod=>prod,(items, item)=> item);
Console.WriteLine("LeftOuterJoin2: {0} items in 1 group", leftOuterQuery2.Count());
// Store the count of total items
int totalItems = 0;
Console.WriteLine("Left Outer Join 2:");
// Groups have been flattened.
foreach (var item in leftOuterQuery2)
{
totalItems++;
Console.WriteLine("{0,-10}{1}", item.Name, item.CategoryID);
}
Console.WriteLine("LeftOuterJoin2: {0} items in 1 group", totalItems);
}
}
/*Output:
InnerJoin:
Cola 1
Tea 1
Mustard 2
Pickles 2
Carrots 3
Bok Choy 3
Peaches 5
Melons 5
InnerJoin: 8 items in 1 group.
Unshaped GroupJoin:
Group:
Cola 1
Tea 1
Group:
Mustard 2
Pickles 2
Group:
Carrots 3
Bok Choy 3
Group:
Group:
Peaches 5
Melons 5
Unshaped GroupJoin: 8 items in 5 unnamed groups
GroupInnerJoin:
Beverages
Cola 1
Tea 1
Condiments
Mustard 2
Pickles 2
Vegetables
Bok Choy 3
Carrots 3
Grains
Fruit
Melons 5
Peaches 5
GroupInnerJoin: 8 items in 5 named groups
GroupJoin3:
Cola:1
Tea:1
Mustard:2
Pickles:2
Carrots:3
Bok Choy:3
Peaches:5
Melons:5
GroupJoin3: 8 items in 1 group
Left Outer Join:
Group:
Cola 1
Tea 1
Group:
Mustard 2
Pickles 2
Group:
Carrots 3
Bok Choy 3
Group:
Nothing! 4
Group:
Peaches 5
Melons 5
LeftOuterJoin: 9 items in 5 groups
LeftOuterJoin2: 9 items in 1 group
Left Outer Join 2:
Cola 1
Tea 1
Mustard 2
Pickles 2
Carrots 3
Bok Choy 3
Nothing! 4
Peaches 5
Melons 5
LeftOuterJoin2: 9 items in 1 group
Press any key to exit.
*/