一、概述
查询运算符是组成Linq模式的方法。这些方法中的大多数都作用于序列;其中序列指其类型实现IEnumberable接口或IQueryable接口的对象。标准查询运算符提供包括筛选、投影、集合、排序等查询功能。
查询运算符包含两组,一组作用于类型IEnumberable的对象,另一组作用于类型IQueryable的对象。
《Linq查询运算符(一)》详细介绍了筛选数据、投影运算、设置运算 、数据排序、限定符运算及数据分区本章将继续介绍。
二、生成运算
2.1 DefaultIfEmpty
返回指定序列的元素;如果序列为空,则返回单一实例集合中的类型参数的默认值。
IList<int> list = new List<int>(); var lst= list.DefaultIfEmpty(); foreach (var item in lst) { Console.WriteLine(item); }
输出结果
0
2.2 Empty
初始化一个Empty空集合
var list=new List() 等于 var list = Enumerable.Empty();
var lst= Enumerable.Empty<int>(); foreach (var item in lst) { Console.WriteLine(item); }
2.3 Range
Range()方法返回IEnumerable类型的集合,该集合具有指定数量的元素和从第一个元素开始的顺序值。
var lst = Enumerable.Range(5, 2); foreach (var item in lst) { Console.WriteLine(item); }
执行结果
5 6
2.4 Repeat
Repeat()方法使用指定数量的元素生成IEnumerable类型的集合,每个元素包含相同的指定值。
var lst = Enumerable.Repeat<double>(5, 2) foreach (var item in lst) { Console.WriteLine(item); }
执行结果
5 5
三、相等运算
3.1 SequenceEqual
两个序列,其相应元素相等且具有被视为相等的相同数量的元素
public class Person { public int Id { get; set; } public string Code { get; set; } public string Name { get; set; } } IList<int> listA = new List<int>() { 1, 2, 3, 4 }; IList<int> listB = new List<int>() { 1, 2, 3, 4 }; IList<int> listC = new List<int>() { 2, 1, 3, 4 }; bool falg= Enumerable.SequenceEqual(listA, listB); Console.WriteLine(falg); bool falgC = Enumerable.SequenceEqual(listA, listC); Console.WriteLine(falgC); IList<Person> peopleA = new List<Person>() { new Person { Id = 2, Code = "002", Name = "CAZ" }, new Person { Id = 2, Code = "002", Name = "CAZ" }, }; IList<Person> peopleB = new List<Person>() { new Person { Id = 2, Code = "002", Name = "CAZ" }, new Person { Id = 2, Code = "002", Name = "CAZ" }, }; bool flagPersonA = Enumerable.SequenceEqual<Person>(peopleA, peopleB); Console.WriteLine(flagPersonA); bool flagPersonB = Enumerable.SequenceEqual<Person>(peopleA, peopleB, new StudentComparer()); Console.WriteLine(flagPersonB); /// <summary> /// 重写比较接口 /// </summary> public class StudentComparer : IEqualityComparer<Person> { public bool Equals(Person? x, Person? y) { if (x.Id == y.Id && x.Code.ToLower() == y.Code.ToLower()) return true; return false; } public int GetHashCode([DisallowNull] Person obj) { return obj.GetHashCode(); } }
四、元素运算
元素运算从序列中返回唯一,特定的元素。
4.1 ElementAt
返回集合中指定索引处的元素。
IList<Person> peopleA = new List<Person>() { new Person { Id = 1, Code = "001", Name = "任峻" }, new Person { Id = 2, Code = "002", Name = "陈矫" }, new Person { Id = 3, Code = "003", Name = "丁仪" }, new Person { Id = 4, Code = "004", Name = "司马朗" }, new Person { Id = 5, Code = "005", Name = "夏侯渊" }, }; Person person= peopleA.ElementAt(4); Console.WriteLine($"Id={person.Id},Code={person.Code},Name={person.Name}");
执行结果
Id=5,Code=005,Name=夏侯渊
4.2 ElementAtOrDefault
返回集合中指定索引处的元素;如果索引超出范围,则返回默认值。
IList<Person> peopleA = new List<Person>() { new Person { Id = 1, Code = "001", Name = "任峻" }, new Person { Id = 2, Code = "002", Name = "陈矫" }, new Person { Id = 3, Code = "003", Name = "丁仪" }, new Person { Id = 4, Code = "004", Name = "司马朗" }, new Person { Id = 5, Code = "005", Name = "夏侯渊" }, }; Person person= peopleA.ElementAtOrDefault<Person>(5); Console.WriteLine($"Id={person?.Id},Code={person?.Code},Name={person?.Name}");
执行结果
Id=,Code=,Name=
4.1 First
返回集合的第一个元素或满足条件的第一个元素。
IList<Person> peopleA = new List<Person>() { new Person { Id = 1, Code = "001", Name = "任峻" }, new Person { Id = 2, Code = "002", Name = "陈矫" }, new Person { Id = 3, Code = "003", Name = "丁仪" }, new Person { Id = 4, Code = "004", Name = "司马朗" }, new Person { Id = 5, Code = "005", Name = "夏侯渊" }, }; Person person= peopleA.First(); Console.WriteLine($"Id={person?.Id},Code={person?.Code},Name={person?.Name}");
执行结果
Id=1,Code=001,Name=任峻
4.2 FirstOrDefault
int[] numbers = new int[] { 1, 2, 3, 5, 7, 11 }; var nums = numbers.FirstOrDefault<int>(e=>e<1); Console.WriteLine($"{ nums}");
执行结果
0
4.3 Last
返回集合的最后一个元素或满足条件的最后一个元素。
int[] numbers = new int[] { 1, 2, 3, 5, 7, 11 }; var nums = numbers.Last(e=>e<5); Console.WriteLine($"{ nums}");
执行结果
3
4.4 LastOrDefault
返回集合的最后一个元素或满足条件的最后一个元素。 如果此类元素不存在,则返回默认值。
int[] numbers = new int[] { 1, 2, 3, 5, 7, 11 }; var nums = numbers.LastOrDefault(e=>e>11); Console.WriteLine($"{ nums}");
执行结果
0
4.5 Single
返回集合的唯一一个元素或满足条件的唯一一个元素。如果没有要返回的元素或要返回多个元素,则引发InvalidOperationException。
int[] numbers = new int[] { 100}; int result = numbers.Single(); Console.WriteLine(result);
执行结果
100
4.6 SingleOrDefault
返回集合的唯一一个元素或满足条件的唯一一个元素。如果没有要返回的元素或要返回多个元素,则引发InvalidOperationException。
int[] numbers = new int[] { 100}; int result = numbers.SingleOrDefault(); Console.WriteLine(result);
执行结果
100
五、转换数据类型
转换数据类型可以通过转换方法更改输入对象的类型。名称以“As”开头的转换方法可更改源集合的静态类型但不枚举(延迟加载)此源集合。名称以“To”开头的方法可枚举(即时加载)源集合并将项放入相应的集合类型。
5.1 AsEnumerable
所有实现了IEnumerable接口的类型都可以调用此方法来获取一个IEnumerable集合。此方法一般仅用于实现类中的方法与IEnumerable接口方法重名时。
var products = db.Product.AsEnumerable() .Select(p => new {p.Id, p.Name, p.CreateTime.Date});
在迭代时遇见AsEnumerable()会先进行Sql查询,对已查出来的结果当然能进行Linq to object操作。
5.2 AsQueryable
AsQueryable将一个序列向下转换 一个IQueryable,它生成了一个本地查询的IQueryable包装。IQueryable实现了IEnumerable接口。但IEnumerable换成IQueryable后速度提高很多。
var query = (from item in context.Users.AsQueryable() where item.id > 10 select item.id).ToList();
5.3 AsEnumerable和AsQueryable
1.AsEnumerable()是延迟执行的,实际上什么都没有发生,当真正使用对象的时候(例如调用:First, Single, ToList…的时候)才执行。
2.AsEnumerable将一个序列向上转换为一个IEnumerable, 强制将Enumerable类下面的查询操作符绑定到后续的子查询当中。
3.AsQueryable将一个序列向下转换为一个IQueryable, 它生成了一个本地查询的IQueryable包装。
4.AsEnumerable()延迟执行,不会立即执行。当你调用.AsEnumerable()的时候,实际上什么都没有发生。
5.ToList()立即执行。
6.当你需要操作结果的时候,用.ToList(),否则,如果仅仅是用来查询不需要进一步使用结果集,并可以延迟执行,就用.AsEnumerable()/IEnumerable /IQueryable。
7.AsEnumerable()虽然延迟执行,但还是访问数据库,而.ToList()直接取得结果放在内存中。比如我们需要显示两个部门的员工时,部门可以先取出放置在List中,然后再依次取出各个部门的员工,这时访问的效率要高一些,因为不需要每次都访问数据库去取出部门。
I8.Queryable实现了IEnumberable接口。但IEnumerable 换成IQueryable后速度提高很多。
9.IEnumerable跑的是Linq to Object,强制从数据库中读取所有数据到内存先。
5.4 Cast
将集合中的元素转换为指定类型。
DataRow row=dt.Rows.Cast<DataRow>().Single();
5.5 OfType
根据其转换为指定类型的能力筛选值。
List<object> objList = new List<object> { new { ID = 1, Name = "阿里" }, new { ID = 2, Name = "百度" }, new Person() { Id=1, Name = "马云", Code="001" } }; var p = objList.OfType<Person>().ToList(); foreach (var item in p) { Console.WriteLine(item.Name); }
5.6 ToArray
将结合转换为数组。此方法强制执行查询。
IList<string> list = new List<string>() { "A", "B", "C" }; string[] arr= list.ToArray();
5.7 ToDictionary
根据键选择器函数将元素放入Dictionary。此方法强制执行查询。
Person[] parameters = new Person[] { new Person() { Id = 1, Code = "052", Name = "正一郎" }, new Person() { Id = 2, Code = "028", Name = "清次郎" }, new Person() { Id = 3, Code = "020", Name = "誠三郎" }, new Person() { Id = 4, Code = "018", Name = "征史郎" }, }; Dictionary<int, Person> dictionary = parameters.ToDictionary(value => value.Id);
5.8 ToList
将集合转换为List。此方法强制执行查询。
string[] strArr = { "1","2", "3" }; IList<string> strList = strArr.ToList();
5.9 ToLookup
根据键选择器函数将元素放入Lookup(一对多字典)。此方法强制执行查询。该ToLookup()是不可改变,一旦创建一个Lookup,不能添加或删除元素。
IList<Person> values = new List<Person>() { new Person { Id=1,Name="0001"}, new Person { Id=2,Name="0002"}, new Person{ Id=3,Name="0003"}, new Person{ Id=4,Name="0004"}, }; var list = values.ToLookup(e => e.Id); foreach (var item in list) { Console.WriteLine(item.Key); }