语言集成查询 (LINQ) 是一系列直接将查询功能集成到 C# 语言的技术统称。
LINQ 通过提供处理各种数据源和数据格式的数据的一致模型,简化了每种数据源或数据格式再查询时使用不同查询语言的情况。
LINQ 查询操作都由以下三个不同的操作组成:
List<int> numQuery2 = (from num in numbers
where (num % 2) == 0
select num).ToList();
from就是获取数据源,其语法是from xx in xxs。from后面跟的时范围变量,in后面就是数据源。
where 是筛选的关键字,其后是筛选条件。可以使用“&&”或“||”等添加很多条件。
例如:
var queryLondonCustomers = from cust in customers
where cust.City == "London"
select cust;
where子句中,除了是一系列的判断外,也可以是返回bool值的方法。即当筛选较复杂时,我们可以先声明一个用于判断每个元素是否符合要求的函数,在where子句中调用该函数。
一次查询中(一个from下),也可以有多个where子句,来实现多次筛选。例如:
var queryLowNums3 = from num in numbers
where num < 5
where num % 2 == 0
select num;
orderby对返回的数据进行排序。有两种的排序:升序和降序,默认的为升序,即数据逐渐增大,或者字母从a到z。默认升序:ascending,可以不写。
降序:descending。
语法例:
// Query for ascending sort.
IEnumerable<string>sortAscendingQuery =
from fruit in fruits
orderby fruit //"ascending" is default
select fruit;
// Query for descending sort.
IEnumerable<string>sortDescendingQuery =
from w in fruits
orderby w descending
select w;
次要排序:orderby中可以填写多个排序选项,用逗号隔开,后面的成为次要排序。例如:
IEnumerable<string> query = from word in words
orderby word.Length, word.Substring(0, 1)
select word;
group即根据所指定的键进行分组。使用 group 子句的查询结果是一个 IGrouping
// queryCustomersByCity 类型实际上是 IEnumerable>
//上面的string类型的来源是cust.City的类型。
var queryCustomersByCity = from cust in customers
group cust by cust.City;
// customerGroup is an IGrouping
foreach (var customerGroup in queryCustomersByCity)
{
Console.WriteLine(customerGroup.Key);
foreach (Customer customer in customerGroup)
{
Console.WriteLine(" {0}", customer.Name);
}
}
分组条件默认是与给定的键值相等,也可以自定义其他条件,即一个以bool类型为返回值的语句,也可以对范围变量进行处理以获得新的键值。
例如(将学生成绩按10分为一档次进行分组):
var studentQuery = from student in students
let avg = (int)student.Scores.Average()
group student by (avg / 10) into g
orderby g.Key
select g;
select 子句生成查询结果并指定每个返回的元素的“形状”或类型。乘称作“选择”或“投影”。换言之,即前面所有的操作之后将数据拆散分组排序后,select即从中挑选所需要的数据并组成需要的类型。例如可以是范围变量或分组后的便变量本身。亦可使用这些变量构造新的类型例如字典、匿名类、其他类,或者是元素的某个字段,或者字段值做一定处理后的值。
//导出每个学生的平均成绩
IEnumerable<double> studentQuery6 = from student in app.students
where student.ID > 111
select student.Scores.Average();
//以学生first和last构成新匿名类的集合
var studentQuery7 = from student in app.students
where student.ID > 111
select new { student.First, student.Last };
//用学生平均成绩和Id构造ScoreInfo对象并填入集合
IEnumerable<ScoreInfo> studentQuery8 = from student in app.students
where student.ID > 111
select new ScoreInfo
{
Average = student.Scores.Average(),
ID = student.ID
};
//找到学生平均成绩大于85的,并在联系列表中更具Id找到联系方式
IEnumerable<ContactInfo> studentQuery9 = from student in app.students
where student.Scores.Average() > 85
join ci in app.contactList on student.ID equals ci.ID
select ci;
可使用 into 创建临时标识符,将 group、join 或 select 子句的结果存储至新标识符。into创建的新标识本身可以继续附加查询命令。 有时称在 group 或 select 子句中使用新标识符为“延续”。
该关键字创建一个新的范围变量并通过提供的表达式结果初始化该变量。简单的说,类似于英文中的“set”,即引入一个新的变量,来代替一些操作。例如前文中group关键字的例子中的那样。
再举一个例子(从一段话中挑出元音开头的单词):
string[] strings =
{
"A penny saved is a penny earned.",
"The early bird catches the worm.",
"The pen is mightier than the sword."
};
// Split the sentence into anarray of words
// and select those whose firstletter is a vowel.
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;
join 子句可将来自不同源序列,并且在对象模型中没有直接关系的元素相关联。 关联的要求是:不同源数据用于关联的元素可以进行比较以判断是否相等。比如:食品经销商拥有的a-某种产品的供应商列表,以及b-买主列表,那么,可以使用 join 子句创建该产品(a)同一指定地区供应商和买主(b)的列表。
在 LINQ 中,仅当两个源序列没有通过任何关系相互联系时,才需要使用显式 join 子句。(因为使用 LINQ to SQL 时,外键表在对象模型中表示为主表的属性。 例如,在 Northwind 数据库中,Customer 表与 Orders 表之间具有外键关系。 将这两个表映射到对象模型时,Customer 类具有一个 Orders 属性,其中包含与该 Customer 相关联的 Orders 集合。 实际上,已经为你执行了联接。)
在连接时,只能使用相等的判断来连接,不能使用“>”或“<”。因此这里避免歧义或错误,只能使用equals关键字。
join分三种联接
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
class Pet
{
public string Name { get; set; }
public Person Owner { get; set; }
}
public static void InnerJoinExample()
{
Person magnus = new Person { FirstName = "Magnus", LastName = "Hedlund" };
Person terry = new Person { FirstName = "Terry", LastName = "Adams" };
Person charlotte = new Person { FirstName = "Charlotte", LastName = "Weiss" };
Person arlene = new Person { FirstName = "Arlene", LastName = "Huff" };
Person rui = new Person { FirstName = "Rui", LastName = "Raposo" };
Pet barley = new Pet { Name = "Barley", Owner = terry };
Pet boots = new Pet { Name = "Boots", Owner = terry };
Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte };
Pet bluemoon = new Pet { Name = "Blue Moon", Owner = rui };
Pet daisy = new Pet { Name = "Daisy", Owner = magnus };
List<Person> people = new List<Person> { magnus, terry, charlotte, arlene, rui };
List<Pet> pets = new List<Pet> { barley, boots, whiskers, bluemoon, daisy };
//people在pets中有匹配的,即人和宠物能对应上的所有成对数据
var query = from person in people
join pet in pets on person equals pet.Owner
select new { OwnerName = person.FirstName, PetName = pet.Name };
foreach (var ownerAndPet in query)
{
Console.WriteLine($"\"{ownerAndPet.PetName}\" is owned by {ownerAndPet.OwnerName}");
}
}
// "Daisy" is owned by Magnus
// "Barley" is owned by Terry
// "Boots" is owned by Terry
// "Whiskers" is owned by Charlotte
// "Blue Moon" is owned by Rui
1.2. 复合键联接
即基于多个属性来比较元素
例如:
IEnumerable<string> query = from employee in employees
join student in students on new { employee.FirstName, employee.LastName }
equals new { student.FirstName, studen LastName }
select employee.FirstName + " " employee.LastName;
1.3. 多联接
可以将任意数量的联接操作相互追加。
例如:
var query = from person in people
join cat in cats on person equals cat.Owner
join dog in dogs on new { Owner = person, Letter = cat.Name.Substring(0, 1) }
equals new { dog.Owner, Letter = dog.Name.Substring(0, 1)}
select new { CatName = cat.Name, DogName = dog.Name };
//数据源等相关代码省略了
var query = from person in people
join pet in pets on person equals pet.Owner into gj
select new { OwnerName = person.FirstName, Pets = gj };
foreach (var v in query)
{
// Output the owner's name.
Console.WriteLine($"{v.OwnerName}:");
// Output each of the owner's pet's names.
foreach (Pet pet in v.Pets)
Console.WriteLine($" {pet.Name}");
}
// Magnus:
// Daisy
// Terry:
// Barley
// Boots
// Blue Moon
// Charlotte:
// Whiskers
// Arlene:
var query = from person in people
join pet in pets on person equals pet.Owner into gj
from subpet in gj.DefaultIfEmpty()
select new { person.FirstName, PetName = subpet?.Name ?? String.Empty };
foreach (var v in query)
{
Console.WriteLine($"{v.FirstName+":",-15}{v.PetName}");
}
// Magnus: Daisy
// Terry: Barley
// Terry: Boots
// Rui: Blue Moon
// Charlotte: Whiskers
// Arlene:
这三种联接,可以类比成一种嵌套循环
和join一起使用,join on
可以参考英语中的用法加以理解
This piece of string is too short. Join another piece on to it. (这条绳子太短,再续上一截儿吧。)
参考:
语言集成查询 (LINQ)
查询关键字(C# 参考)