LINQ标准查询操作符的使用介绍
一、投影操作符
1. Select
Select操作符对单个序列或集合中的值进行投影。下面的示例中使用select从序列中返回Employee表的所有列:
using (NorthwindDataContext db=new NorthwindDataContext())
{
//查询语法
var query =
from e in db.Employees
where e.FirstName.StartsWith("M")
select e;
//方法语法
var q =
db.Employees
.Where(e => e.FirstName.StartsWith("M"))
.Select(e => e);
foreach (var item in query)
{
Console.WriteLine(item.FirstName);
}
}
当然,你也可以返回单个列,例如:
var query =
from e in db.Employees
where e.FirstName.StartsWith("M")
select e.FirstName;
你也可以返回序列中的某几列,例如:
var query =
from e in db.Employees
where e.FirstName.StartsWith("M")
select new
{
e.FirstName,
e.LastName,
e.Title
};
2. SelectMany
SelectMany操作符提供了将多个from子句组合起来的功能,它将每个对象的结果合并成单个序列。下面是一个示例:
using (NorthwindDataContext db=new NorthwindDataContext())
{
//查询语法
var query =
from e in db.Employees
from o in e.Orders
select o;
//方法语法
var q =
db.Employees
.SelectMany(e => e.Orders);
foreach (var item in query)
{
Console.WriteLine(item.Freight);
}
}
二、限制操作符
Where是限制操作符,它将过滤标准应用在序列上,按照提供的逻辑对序列中的数据进行过滤。
Where操作符不启动查询的执行。当开始对序列进行遍历时查询才开始执行,此时过滤条件将被应用到查询中。Where操作符的使用方法已经在第一节中出现过,这里不再冗述。
三、排序操作符
排序操作符,包括OrderBy、OrderByDescending、ThenBy、ThenByDescending和Reverse,提供了升序或者降序排序。
1. OrderBy
OrderBy操作符将序列中的元素按照升序排列。下面的示例演示了这一点:
using (NorthwindDataContext db = new NorthwindDataContext())
{
//查询语法
var query =
from e in db.Employees
orderby e.FirstName
select e;
//方法语法
var q =
db.Employees
.OrderBy(e => e.FirstName)
.Select(e => e);
foreach (var item in q)
{
Console.WriteLine(item.FirstName);
}
}
这里可以使用OrderBy的重载方法OrderBy(Func
2. OrderByDescending
OrderByDescending操作符将序列中的元素按照降序排列。用法与OrderBy相同,这里不再演示。
3. ThenBy
ThenBy操作符实现按照次关键字对序列进行升序排列。此操作符的查询语法与方法语法略有不同,以下代码演示了这一点:
using (NorthwindDataContext db = new NorthwindDataContext())
{
//查询语法
var query =
from e in db.Employees
orderby e.FirstName,e.LastName
select e;
//方法语法
var q =
db.Employees
.OrderBy(e => e.FirstName)
.ThenBy(e => e.LastName)
.Select(e => e);
foreach (var item in query)
{
Console.WriteLine(item.FirstName);
}
}
4. ThenByDescending
ThenByDescending操作符实现按照次关键字对序列进行降序排列。此操作符的查询语法与方法语法略有不同,以下代码演示了这一点:
using (NorthwindDataContext db = new NorthwindDataContext())
{
//查询语法
var query =
from e in db.Employees
orderby e.FirstName,e.LastName descending
select e;
//方法语法
var q =
db.Employees
.OrderBy(e => e.FirstName)
.ThenByDescending(e => e.LastName)
.Select(e => e);
foreach (var item in query)
{
Console.WriteLine(item.FirstName);
}
}
5. Reverse
Reverse将会把序列中的元素按照从后到前的循序反转。需要注意的是,Reverse方法的返回值是void,以下代码演示了这一点:
using (NorthwindDataContext db = new NorthwindDataContext())
{
//方法语法
var q =
db.Employees
.Select(e => e.FirstName)
.ToList();
q.Reverse();
foreach (var item in q)
{
Console.WriteLine(item);
}
}
四、联接操作符
联接是指将一个数据源对象与另一个数据源对象进行关联或者联合的操作。这两个数据源对象通过一个共同的值或者属性进行关联。
LINQ有两个联接操作符:Join和GroupJoin。
1. Join
Join操作符类似于T-SQL中的inner join,它将两个数据源相联接,根据两个数据源中相等的值进行匹配。例如,可以将产品表与产品类别表相联接,得到产品名称和与其相对应的类别名称。以下的代码演示了这一点:
using (NorthwindDataContext db = new NorthwindDataContext())
{
db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中
//查询语法
var query =
from p in db.Products
join c in db.Categories on p.CategoryID equals c.CategoryID
where p.CategoryID == 1
select p;
//方法语法
var q =
db.Products
.Join
(
db.Categories,
p => p.CategoryID,
c => c.CategoryID,
(p, c) => p
)
.Where(p => p.CategoryID == 1);
foreach (var item in query)
{
Console.WriteLine(item.ProductName);
}
}
以上代码为表述清晰加入了一个条件“where p.CategoryID == 1”,即仅返回产品类别ID为1的所有产品。
2. GroupJoin
GroupJoin操作符常应用于返回“主键对象-外键对象集合”形式的查询,例如“产品类别-此类别下的所有产品”。以下的代码演示了这一点:
using (NorthwindDataContext db = new NorthwindDataContext())
{
db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中
//查询语法
var query =
from c in db.Categories
join p in db.Products on c.CategoryID equals p.CategoryID into r
select new
{
c.CategoryName,
Products = r
};
//方法语法
var q =
db.Categories
.GroupJoin
(
db.Products,
c => c.CategoryID,
p => p.CategoryID,
(c, p) => new
{
c.CategoryName,
Products = p
}
);
foreach (var item in query)
{
Console.WriteLine("{0} =>", item.CategoryName);
foreach (var p in item.Products)
{
Console.WriteLine(p.ProductName);
}
Console.WriteLine("----------------------------------------------");
}
}
五、分组操作符
分组是根据一个特定的值将序列中的元素进行分组。LINQ只包含一个分组操作符:GroupBy。
下面的示例中使用了产品表,以CategoryID作为分组关键值,按照产品类别对产品进行了分组。
using (NorthwindDataContext db = new NorthwindDataContext())
{
db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中
//查询语法
var query =
from p in db.Products
group p by p.CategoryID;
//方法语法
var q =
db.Products
.GroupBy(p => p.CategoryID);
foreach (var item in query)
{
Console.WriteLine("{0} =>", item.Key);
foreach (var p in item)
{
Console.WriteLine(p.ProductName);
}
Console.WriteLine("----------------------------------------------");
}
}
执行GroupBy得到的序列中包含的元素类型为IGrouping
六、串联操作符
串联是一个将两个集合联接在一起的过程。在LINQ中,这个过程通过Concat操作符来实现。
在下面的示例中,将会把类别名称串联在产品名称之后:
using (NorthwindDataContext db = new NorthwindDataContext())
{
db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中
//方法语法
var q =
db.Products
.Select(p => p.ProductName)
.Concat
(
db.Categories.Select(c => c.CategoryName)
);
foreach (var item in q)
{
Console.WriteLine(item);
}
}
七、聚合操作符
聚合函数将在序列上执行特定的计算,并返回单个值,如计算给定序列平均值、最大值等。共有7种LINQ聚合查询操作符:Aggregate、Average、Count、LongCount、Max、Min和Sum。
1. Aggregate
Aggregate操作符对集合值执行自定义聚合运算。例如,需要列出所有产品类别清单,每个类别名称之间用顿号连接。以下的代码演示了这一过程:
using (NorthwindDataContext db = new NorthwindDataContext())
{
db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中
//方法语法
var q =
db.Categories
.Select(c => c.CategoryName)
.ToArray()
.Aggregate((current, next) => String.Format("{0}、{1}", current, next));
Console.WriteLine(q);
}
如果你对这一过程有些迷惑,那么请参照以下代码:
var query =
db.Categories
.Select(c => c.CategoryName)
.ToArray();
string r = String.Empty;
foreach (var item in query)
{
r += "、";
r += item;
}
r = r.Substring(1); //去除第一个顿号
Console.WriteLine(r);
2. Average
求集合中元素的平均值,返回值类型double
3. Count
求集合中元素的个数,返回值类型Int32
4. LongCount
求集合中元素的个数,返回值类型Int64
5. Max
求集合中元素的最大值
6. Min
求集合中元素的最小值
7. Sum
求集合中元素的和
八、集合操作符
LINQ 中的集合操作符是指根据相同或不同集合(或集)中是否存在等效元素来生成结果集的查询操作,一共有4种:
使用方式均为“集合1.方法名(集合2)”,返回值为运算结果的集合,这里就不演示了。
九、生成操作符
生成是指创建新的值序列。
1. Empty
Empty操作符返回一个指定类型的空集合。这里的空不是null,而是元素数量为0的集合。以下的示例演示了如何创建一个IEnumerable<int>类型的空集合:
var q = Enumerable.Empty<int>();
Console.WriteLine(q == null);
Console.WriteLine(q.Count());
2. DefaultIfEmpty
DefaultIfEmpty将空集合替换为具有默认值的单一实例集合。执行此方法获得的集合将至少含有一个元素,这是因为DefaultIfEmpty方法需要两个参数,第一个参数是一个泛型集合,第二个参数是相应类型的单个元素,如果第一个参数中不含有任何元素,它将返回第二个参数指定的单个元素。如果你使用了DefaultIfEmpty方法的重载方法DefaultIfEmpty<T>(IEnumerable<T> array),如果指定的array集合为空,那么将返回一个类型为T,值为null的单个对象。以下的代码演示了这一过程:
代码如下:
using (NorthwindDataContext db = new NorthwindDataContext())
{
db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中
//方法语法
var q =
Enumerable.DefaultIfEmpty
(
db.Employees
.Where(e => e.FirstName.StartsWith("Aaf")) //更改此处的条件可获得不同的集合
, new Employees() { FirstName = "Sunny D.D" }
);
Console.WriteLine(q.Count());
foreach (var item in q)
{
Console.WriteLine(item.FirstName);
}
}
3. Range
Range操作符用于生成指定范围内的整数的序列。它需要两个参数,第一个参数是序列开始的整数值,第二个参数是序列中整数的数量。下面的示例演示了使用Range操作符来生成从0到9的整数序列:
var q =
Enumerable.Range(0, 10);
foreach (var item in q)
{
Console.WriteLine(item);
}
4. Repeat
Repeat操作符用于生成包含一个重复值的集合。它需要两个参数,第一个参数是任意类型的元素,第二个参数是生成的序列中所包含此元素的数量。下面的示例演示了使用Repeat来生成一个包含10个0的序列:
var q =
Enumerable.Repeat(0, 10);
foreach (var item in q)
{
Console.WriteLine(item);
}
十、转换操作符
转换操作符是用来实现将输入对象的类型转变为序列的功能。名称以“As”开头的转换方法可更改源集合的静态类型但不枚举(延迟加载)此源集合。名称以“To”开头的方法可枚举(即时加载)源集合并将项放入相应的集合类型。
1. AsEnumerable
所有实现了IEnumerable
class AsEnumerableTest
{
public void Where(Func
{
Console.WriteLine("AsEnumerableTest的Where方法");
}
}
public static void AsEnumerable()
{
AsEnumerableTest
q.Where(r => r //q.AsEnumerable().Where(r => r //IEnumerable
//i.Where(r => r }
2. Cast
Cast
如果集合中的元素无法强制转换为 T 类型,则此方法将引发异常。以下代码演示了这一过程:
ArrayList array = new ArrayList();
array.Add("Bob");
array.Add("Jack");
array.Add(1);
foreach (var item in array.Cast
{
Console.WriteLine(item);
}
运行此代码,可以输出“Bob”、“Jack”,然后会报出一个异常“无法将int强制转换为string”,这说明Cast方法也是延迟执行实现的,只有在枚举过程中才将对象逐个强制转换为T类型。
3. OfType
OfType
4. ToArray
ToArray 操作符可以在IEnumerable
5. ToDictionary
ToDictionary操作符根据指定的键选择器函数,从IEnumerable
using (NorthwindDataContext db = new NorthwindDataContext())
{
db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中
//方法语法
var q =
db.Categories
.ToDictionary
(
c => c.CategoryID,
c => c.CategoryName
);
foreach (var item in q)
{
Console.WriteLine("{0} - {1}",item.Key,item.Value);
}
}
需要注意的是,如果省略ToDictionary方法的第二个参数(值选择函数),那么Value将会保存一个类别对象。还有,如果Key为null,或者出现重复的Key,都将导致抛出异常。
6. ToList
ToList操作符可以在IEnumerable
7. ToLookup
ToLookup操作符将创建一个 Lookup
using (NorthwindDataContext db = new NorthwindDataContext())
{
db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中
//方法语法
var q =
db.Products
.ToLookup
(
p => p.CategoryID,
p => p.ProductName
);
foreach (var item in q)
{
Console.WriteLine(item.Key);
foreach (var p in item)
{
Console.WriteLine(p);
}
}
}
可以看出,ToLookup操作与GroupBy操作很相似,只不过GroupBy是延迟加载的,而ToLookup是即使加载。
十一、元素操作符
元素操作符将从一个序列中返回单个指定的元素。
1. First
First操作将返回序列中的第一个元素。如果序列中不包含任何元素,则First
using (NorthwindDataContext db = new NorthwindDataContext())
{
db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中
//无参
var query =
db.Employees
.First();
//有参
var q =
db.Employees
.First(e => e.FirstName.StartsWith("S"));
Console.WriteLine(q.FirstName);
}
上述代码中使用了First
2. FirstOrDefault
FirstOrDefault方法将返回序列中的第一个元素;如果序列中不包含任何元素,则返回默认值。它也可以像First方法一样传递一个条件。需要说明的是如果序列中不包含任何元素,返回的默认值是个怎样的元素。在这之前,先来看一下FirstOrDefault方法是如何实现的:
public static TSource FirstOrDefault
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
IList
if (list != null)
{
if (list.Count > 0)
{
return list[0];
}
}
else
{
using (IEnumerator
{
if (enumerator.MoveNext())
{
return enumerator.Current;
}
}
}
return default(TSource);
}
1. 如果调用FirstOrDefault方法的序列为空,抛出异常
2. 如果序列成功转换为List
3. 如果序列没有成功转换为List
4. 如果上述操作都没有执行,则使用default(T)关键字返回类型T的默认值
以下给出MSDN中,对于default(T)关键字的描述:
在泛型类和泛型方法中产生的一个问题是,在预先未知以下情况时,如何将默认值分配给参数化类型 T:
T 是引用类型还是值类型。
如果 T 为值类型,则它是数值还是结构。
给定参数化类型 T 的一个变量 t,只有当 T 为引用类型时,语句 t = null 才有效;只有当 T 为数值类型而不是结构时,语句 t = 0 才能正常使用。解决方案是使用 default 关键字,此关键字对于引用类型会返回 null,对于数值类型会返回零。对于结构,此关键字将返回初始化为零或 null 的每个结构成员,具体取决于这些结构是值类型还是引用类型。
3. Last
Last方法将返回序列中的最后一个元素。使用方法参照First。
4. LastOrDefault
LastOrDefault方法将返回序列中的最后一个元素;如果序列中不包含任何元素,则返回默认值。使用方法参照FirstOrDefault。
5. ElementAt
ElementAt方法返回序列中指定索引处的元素。使用方法参照First。需要注意的是如果索引超出范围会导致异常。
6. ElementAtOrDefault
ElementAtOrDefault方法将返回序列中指定索引处的元素;如果索引超出范围,则返回默认值。使用方法参照FirstOrDefault。
7. Single
Single方法的无参形式将从一个序列中返回单个元素,如果该序列包含多个元素,或者没有元素数为0,则会引发异常。也就是说,在序列执行Single方法的无参形式时,必须保证该序列有且仅有一个元素。
Single方法的有参形式将从一个序列中返回符合指定条件的唯一元素,如果有多个元素,或者没有元素符合这一条件,则会引发异常。以下代码演示了Single的使用方式:
using (NorthwindDataContext db = new NorthwindDataContext())
{
db.Log = Console.Out; //将生成的T-SQL语句输出到控制台中
//方法语法
var q =
db.Employees
.Single();
var query =
db.Employees
.Single(e => e.FirstName.StartsWith("S"));
Console.WriteLine(query.FirstName);
}
8. SingleOrDefault
SingleOrDefault方法的无参形式将从一个序列中返回单个元素。如果元素数为0,则返回默认值。如果该序列包含多个元素,则会引发异常。
SingleOrDefault方法的有参形式将从一个序列中返回符合指定条件的唯一元素,如果元素数为0,则返回默认值;如果该序列包含多个元素,则会引发异常。SingleOrDefault的使用方式与Single相同。
需要注意的是,Single方法与SingleOrDefault方法都是即时加载的,在代码进行到方法所在位置时,如果引发了异常,会立刻抛出。
十二、相等操作符
如果两个序列的对应元素相等且这两个序列具有相同数量的元素,则视这两个序列相等。
SequenceEqual方法通过并行地枚举两个数据源并比较相应元素来判断两个序列是否相等。如果两个序列完全相等,返回true,否则返回false。以下代码是SequenceEqual方法的实现过程:
public static bool SequenceEqual
{
if (comparer == null)
{
comparer = EqualityComparer
}
if (first == null)
{
throw Error.ArgumentNull("first");
}
if (second == null)
{
throw Error.ArgumentNull("second");
}
using (IEnumerator
{
using (IEnumerator
{
while (enumerator.MoveNext())
{
if (!enumerator2.MoveNext() || !comparer.Equals(enumerator.Current, enumerator2.Current))
{
return false;
}
}
if (enumerator2.MoveNext())
{
return false;
}
}
}
return true;
}
以上代码的执行过程为:
1. 如果比较器为null,赋值为默认值EqualityComparer
2. 如果序列1为null,抛出异常。
3. 如果序列2为null,抛出异常。
4. 遍历序列1。在此过程中,如果序列2到达底端则返回false;如果序列1的当前值与序列2的当前值不同,则返回false。
5. 序列1遍历完成后,如果序列2未到达底端,则返回false。
6. 如果第2-5步都没有执行,则返回true。
十三、限定操作符
限定符运算返回一个 Boolean 值,该值指示序列中是否有一些元素满足条件或是否所有元素都满足条件。
下图描述了两个不同源序列上的两个不同限定符运算。第一个运算询问是否有一个或多个元素为字符“A”,结果为 true。第二个运算询问是否所有元素都为字符“A”,结果为 true。
1. All
All方法用来确定是否序列中的所有元素都满足条件。以下代码演示了All的用法:
代码如下:
string[] source1 = new string[] { "A", "B", "C", "D", "E", "F" };
string[] source2 = new string[] { "A", "A", "A", "A", "A", "A" };
Console.WriteLine(source1.All(w => w == "A")); //console will print "False"
Console.WriteLine(source2.All(w => w == "A")); //console will print "True"
2. Any
Any方法的无参方式用来确定序列是否包含任何元素。如果源序列包含元素,则为 true;否则为 false。
Any方法的有参方式用来确定序列中是否有元素满足条件。只要有一个元素符合指定条件即返回true,如果一个符合指定条件的元素都没有则返回false。以下代码演示了Any方法有参方式的用法:
代码如下:
string[] source1 = new string[] { "A", "B", "C", "D", "E", "F" };
string[] source2 = new string[] { "A", "A", "A", "A", "A", "A" };
Console.WriteLine(source1.Any(w => w == "A")); //console will print "True"
Console.WriteLine(source2.Any(w => w == "A")); //console will print "True"
3. Contains
Contains方法用来确定序列是否包含满足指定条件的元素。如果有返回true,否则返回false。以下代码使用默认的String比较器来判断序列中是否含有指定的字符串:
代码如下:
string[] source1 = new string[] { "A", "B", "C", "D", "E", "F" };
Console.WriteLine(source1.Contains("A")); //console will print "True"
Console.WriteLine(source1.Contains("G")); //console will print "False"
如果要对序列中的元素进行自定义比较,需要一个IEqualityComparer
十四、分区操作符
LINQ 中的分区指的是在不重新排列元素的情况下,将输入序列划分为两部分,然后返回其中一个部分的操作。
下图显示对一个字符序列执行三个不同的分区操作的结果。第一个操作返回序列中的前三个元素。第二个操作跳过前三个元素,返回剩余的元素。第三个操作跳过序列中的前两个元素,返回接下来的三个元素。
1. Take
Take(int n)方法将从序列的开头返回数量为n的连续元素。以下代码演示了从一个序列中返回其前五个元素:
代码如下:
int[] source = new int[] { 86, 2, 77, 94, 100, 65, 5, 22, 70, 55, 81, 66, 45 };
var q = source.Take(5);
foreach (var item in q)
{
Console.WriteLine(item);
}
上述代码的运行结果为下图所示:
2. TakeWhile
TakeWhile方法执行时将逐个比较序列中的每个元素是否满足指定条件,直到碰到不符合指定的条件的元素时,返回前面所有的元素组成的序列。以下代码演示了这一过程:
代码如下
int[] source = new int[] { 86, 2, 77, 94, 100, 65, 5, 22, 70, 55, 81, 66, 45 };
var q = source.TakeWhile(i => i foreach (var item in q)
{
Console.WriteLine(item);
}
上述代码的运行结果为下图所示:
3. Skip
Skip(int n)方法将跳过序列开头的n个元素,然后返回其余的连续元素。以下代码演示了从一个序列中跳过前五个元素,然后返回其余的元素组成的序列:
代码如下
int[] source = new int[] { 86, 2, 77, 94, 100, 65, 5, 22, 70, 55, 81, 66, 45 };
var q = source.Skip(5);
foreach (var item in q)
{
Console.WriteLine(item);
}
上述代码的运行结果为下图所示:
4. SkipWhile
SkipWhile方法执行时将逐个比较序列中的每个元素是否满足指定条件,直到碰到不符合指定的条件的元素时,返回其余所有的元素组成的序列。以下代码演示了这一过程:
代码如下:
int[] source = new int[] { 86, 2, 77, 94, 100, 65, 5, 22, 70, 55, 81, 66, 45 };
var q = source.SkipWhile(i => i foreach (var item in q)
{
Console.WriteLine(item);
}
上述代码的运行结果为下图所示:
LINQ标准查询操作符的使用介绍小编就暂时分享到这里了,还有哪些地方不懂的,欢迎来网站留言。小编每天都不定时更新文章,如果你觉得不错可以随时来关注下爱站技术频道网站。
上一篇:ASP.NET操作事务介绍