您的位置:首页 > 其它

Linq

2014-01-02 16:02 302 查看


原地址:http://www.51aspx.com/Subject/LINQ


基本 LINQ 查询操作

本主题简要介绍 LINQ 查询表达式,以及您在查询中执行的一些典型类型的操作。 下面各主题中提供了更详细的信息:

LINQ 查询表达式(C# 编程指南)

标准查询运算符概述

注意

如果您已熟悉查询语言(如 SQL 或 XQuery),则可以跳过本主题的大部分内容。 阅读下一节中的“from 子句”来了解 LINQ 查询表达式中的子句的顺序。

获取数据源

--------------------------------------------------------------------------------

在 LINQ 查询中,第一步是指定数据源。 像在大多数编程语言中一样,在 C# 中,必须先声明变量,才能使用它。 在 LINQ 查询中,最先使用 from 子句的目的是引入数据源 (customers) 和范围变量 (cust)。

C#复制

//queryAllCustomers is an IEnumerable<Customer>

var queryAllCustomers = from cust in customers

select cust;

范围变量类似于 foreach 循环中的迭代变量,但在查询表达式中,实际上不发生迭代。 执行查询时,范围变量将用作对 customers 中的每个后续元素的引用。 因为编译器可以推断 cust 的类型,所以您不必显式指定此类型。 其他范围变量可由 let 子句引入。
有关更多信息,请参见 let 子句(C# 参考)。

注意

对于非泛型数据源(如 ArrayList),必须显式类型化范围变量。 有关更多信息,请参见如何:使用 LINQ 查询 ArrayList和 from 子句(C# 参考)。

筛选

--------------------------------------------------------------------------------

也许最常用的查询操作是应用布尔表达式形式的筛选器。 此筛选器使查询只返回那些表达式结果为 true 的元素。 使用 where 子句生成结果。 实际上,筛选器指定从源序列中排除哪些元素。 在下面的示例中,只返回那些地址位于伦敦的 customers。

C#复制

var queryLondonCustomers = from cust in customers

where cust.City == "London"

select cust;

您可以使用熟悉的 C# 逻辑 AND 和 OR 运算符来根据需要在 where 子句中应用任意数量的筛选表达式。 例如,若要只返回位于“伦敦”AND 姓名为“Devon”的客户,您应编写下面的代码:

C#复制

where cust.City=="London" && cust.Name == "Devon"

若要返回位于伦敦或巴黎的客户,您应编写下面的代码:

C#复制

where cust.City == "London" || cust.City == "Paris"

有关更多信息,请参见 where 子句(C# 参考)。

Ordering

--------------------------------------------------------------------------------

通常可以很方便地将返回的数据进行排序。 orderby 子句将使返回的序列中的元素按照被排序的类型的默认比较器进行排序。 例如,下面的查询可以扩展为按 Name 属性对结果进行排序。 因为 Name 是一个字符串,所以默认比较器执行从 A 到 Z 的字母排序。

C#复制

var queryLondonCustomers3 =

from cust in customers

where cust.City == "London"

orderby cust.Name ascending

select cust;

若要按相反顺序(从 Z 到 A)对结果进行排序,请使用 orderby…descending 子句。

有关更多信息,请参见 orderby 子句(C# 参考)。

分组

--------------------------------------------------------------------------------

使用 group 子句,您可以按指定的键分组结果。 例如,您可以指定结果应按 City 分组,以便位于伦敦或巴黎的所有客户位于各自组中。 在本例中,cust.City 是键。

注意

在下面的示例中,类型是显式的以更好地说明概念。 您也可以对 custQuery、group 和 customer 使用隐式类型以让编译器确定准确的类型。

C#复制

// queryCustomersByCity is an IEnumerable<IGrouping<string, Customer>>

var queryCustomersByCity =

from cust in customers

group cust by cust.City;

// customerGroup is an IGrouping<string, Customer>

foreach (var customerGroup in queryCustomersByCity)

{

Console.WriteLine(customerGroup.Key);

foreach (Customer customer in customerGroup)

{

Console.WriteLine(" {0}", customer.Name);

}

}

在使用 group 子句结束查询时,结果采用列表的列表形式。 列表中的每个元素是一个具有 Key 成员及根据该键分组的元素列表的对象。 在循环访问生成组序列的查询时,您必须使用嵌套的 foreach 循环。 外部循环用于循环访问每个组,内部循环用于循环访问每个组的成员。

如果您必须引用组操作的结果,可以使用 into 关键字来创建可进一步查询的标识符。 下面的查询只返回那些包含两个以上的客户的组:

C#复制

// custQuery is an IEnumerable<IGrouping<string, Customer>>

var custQuery =

from cust in customers

group cust by cust.City into custGroup

where custGroup.Count() > 2

orderby custGroup.Key

select custGroup;

有关更多信息,请参见 group 子句(C# 参考)。

联接

--------------------------------------------------------------------------------

联接运算创建数据源中没有显式建模的序列之间的关联。 例如,您可以执行联接来查找位于同一地点的所有客户和经销商。 在 LINQ 中,join 子句始终针对对象集合而非直接针对数据库表运行。

C#复制

var innerJoinQuery =

from cust in customers

join dist in distributors on cust.City equals dist.City

select new { CustomerName = cust.Name, DistributorName = dist.Name };

在 LINQ 中,您不必像在 SQL 中那样频繁使用 join,因为 LINQ 中的外键在对象模型中表示为包含项集合的属性。 例如,Customer 对象包含 Order 对象的集合。 不必执行联接,只需使用点表示法访问订单:

other复制

from order in Customer.Orders...


使用 LINQ 进行数据转换 (C#)

语言集成查询 (LINQ) 不仅可用于检索数据,

而且还是一个功能强大的数据转换工具。

通过使用 LINQ 查询,您可以将源序列用作输入,并采用多种方式修改它以创建新输出序列。

您可以通过排序和分组来修改序列本身,而不必修改元素本身。但是,LINQ 查询的最强大功能可能在于它能够创建新类型。

这一功能在

select

子句中实现。

例如,可以执行下列任务:

·

将多个输入序列合并到具有新类型的单个输出序列中。

·

创建其元素只包含源序列中的各个元素的一个或几个属性的输出序列。

·

创建其元素包含对源数据执行的操作结果的输出序列。

·

创建不同格式的输出序列。

例如,您可以将 SQL 行或文本文件的数据转换为 XML。

这只是几个示例。

当然,可以采用多种方式将这些转换组合在同一查询中。

另外,一个查询的输出序列可用作新查询的输入序列。

将多个输入联接到一个输出序列

可以使用 LINQ 查询来创建包含多个输入序列的元素的输出序列。

下面的示例演示如何组合两个内存中的数据结构,但组合来自 XML 或 SQL 或数据集源的数据时可应用相同的原则。

假定下面两种类类型:

C#

class Student

{

public

string First
{ get; set;
}

public

string Last
{get; set;}

public

int ID
{ get; set;
}

public

string Street
{ get; set;
}

public

string City
{ get; set;
}

public List<int>
Scores;

}

class Teacher

{

public

string First
{ get; set;
}

public

string Last
{ get; set;
}

public

int ID
{ get; set;
}

public

string City
{ get; set;
}

}

下面的示例演示该查询:

C#

class DataTransformations

{

static

void Main()

{

//
Create the first data source.

List<Student> students = new List<Student>()

{

new Student
{First="Svetlana",

Last="Omelchenko",

ID=111,

Street="123
Main Street",

City="Seattle",

Scores= new List<int>
{97, 92, 81, 60}},

new Student
{First="Claire",

Last="O’Donnell",

ID=112,

Street="124
Main Street",

City="Redmond",

Scores= new List<int>
{75, 84, 91, 39}},

new Student
{First="Sven",

Last="Mortensen",

ID=113,

Street="125
Main Street",

City="Lake
City",

Scores= new List<int>
{88, 94, 65, 91}},

};

//
Create the second data source.

List<Teacher> teachers = new List<Teacher>()

{

new Teacher
{First="Ann",
Last="Beebe",
ID=945, City = "Seattle"},

new Teacher
{First="Alex",
Last="Robinson",
ID=956, City = "Redmond"},

new Teacher
{First="Michiyo",
Last="Sato",
ID=972, City = "Tacoma"}

};

//
Create the query.

var peopleInSeattle
= (from student in students

where student.City
== "Seattle"

select student.Last)

.Concat(from teacher in teachers

where teacher.City
== "Seattle"

select teacher.Last);

Console.WriteLine("The
following students and teachers live in Seattle:");

//
Execute the query.

foreach (var person in peopleInSeattle)

{

Console.WriteLine(person);

}

Console.WriteLine("Press
any key to exit.");

Console.ReadKey();

}

}

/* Output:

The following students and teachers live in Seattle:

Omelchenko

Beebe

*/

选择各个源元素的子集

选择源序列中的各个元素的子集有两种主要方法:

1.

若要只选择源元素的一个成员,请使用点运算。

在下面的示例中,假定 Customer 对象包含几个公共属性,其中包括名为 City 的字符串。

在执行此查询时,此查询将生成字符串输出序列。

other

var query
= from cust in Customers

select cust.City;

2.

若要创建包含源元素的多个属性的元素,可以使用具有命名对象或匿名类型的对象初始值设定项。

下面的示例演示如何使用匿名类型来封装各个 Customer 元素的两个属性:

other

var query
= from cust in Customer

select new {Name = cust.Name, City = cust.City};

将内存中的对象转换为 XML

通过 LINQ 查询,可以轻松地在内存中的数据结构、SQL 数据库、ADO.NET 数据集和 XML 流或文档之间转换数据。

下面的示例将内存中的数据结构中的对象转换为 XML 元素。

C#

class XMLTransform

{

static

void Main()

{

//
Create the data source by using a collection initializer.

List<Student> students = new List<Student>()

{

new Student
{First="Svetlana",
Last="Omelchenko",
ID=111, Scores = new List<int>{97,
92, 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>{88,
94, 65, 91}},

};

//
Create the query.

var studentsToXML
= new XElement("Root",

from student in students

let x
= String.Format("{0},{1},{2},{3}",
student.Scores[0],

student.Scores[1], student.Scores[2], student.Scores[3])

select

new XElement("student",

new XElement("First",
student.First),

new XElement("Last",
student.Last),

new XElement("Scores",
x)

) //
end "student"

); //
end "Root"

//
Execute the query.

Console.WriteLine(studentsToXML);

//
Keep the console open in debug mode.

Console.WriteLine("Press
any key to exit.");

Console.ReadKey();

}

}

此代码生成下面的 XML 输出:

other

< Root>

<student>

<First>Svetlana</First>

<Last>Omelchenko</Last>

<Scores>97,92,81,60</Scores>

</student>

<student>

<First>Claire</First>

<Last>O'Donnell</Last>

<Scores>75,84,91,39</Scores>

</student>

<student>

<First>Sven</First>

<Last>Mortensen</Last>

<Scores>88,94,65,91</Scores>

</student>

</Root>

对源元素执行操作



输出序列可能不包含源序列的任何元素或元素属性。

输出可能是通过将源元素用作输入参数计算出的值的序列。

在执行下面这个简单查询时,此查询会输出一个字符串序列,该序列值表示根据 double 类型的元素的源序列进行的计算。
注意

如果查询将转换为某个其他域,则不支持在查询表达式中调用方法。

例如,不能在 LINQ
to SQL 中调用一般 C# 方法,因为 SQL Server 没有该方法的上下文。

但是,可以将存储过程映射到方法,然后调用方法。

C#

class FormatQuery

{

static

void Main()

{

//
Data source.

double[]
radii = { 1, 2, 3 };

//
Query.

IEnumerable<string>
query =

from rad in radii

select String.Format("Area
= {0}",
(rad * rad) * 3.14);

//
Query execution.

foreach (string s in query)

Console.WriteLine(s);

//
Keep the console open in debug mode.

Console.WriteLine("Press
any key to exit.");

Console.ReadKey();

}

}

/* Output:

Area = 3.14

Area = 12.56

Area = 28.26

*/


LINQ 和泛型类型 (C#)

LINQ 查询基于泛型类型,在 .NET Framework 的 2.0 版中引入了泛型类型。

您无需深入了解泛型即可开始编写查询。

但是,您可能需要了解两个基本概念:

1.

当您创建泛型集合类(如

List(Of
T))的实例时,您将“T”替换为列表将包含的对象的类型。

例如,字符串列表表示为 List<string>,Customer 对象列表表示为 List<Customer>。

泛型列表是强类型的,且提供了比将其元素存储为

Object

的集合更多的好处。

如果您尝试将 Customer 添加到 List<string>,则会在编译时出现一条错误。

泛型集合易于使用的原因是您不必执行运行时类型强制转换。

2.

IEnumerable(Of
T)

是一个接口,通过该接口,可以使用 foreach 语句来枚举泛型集合类。

泛型集合类支持

IEnumerable(Of
T),就像非泛型集合类(如

ArrayList)支持

IEnumerable

LINQ 查询中的 IEnumerable<T> 变量

LINQ 查询变量类型化为

IEnumerable(Of
T)

或派生类型,如

IQueryable(Of
T)。

当您看到类型化为 IEnumerable<Customer> 的查询变量时,这只意味着在执行该查询时,该查询将生成包含零个或多个 Customer 对象的序列。

C#

IEnumerable<Customer>
customerQuery =

from cust in customers

where cust.City
== "London"

select cust;

foreach (Customer
customer in customerQuery)

{

Console.WriteLine(customer.LastName + ",
" +
customer.FirstName);

}

让编译器处理泛型类型声明

如果您愿意,可以使用

var

关键字来避免使用泛型语法。

var 关键字指示编译器通过查看在 from 子句中指定的数据源来推断查询变量的类型。

下面的示例生成与上一个示例相同的编译代码:

C#

var customerQuery2
=

from cust in customers

where cust.City
== "London"

select cust;

foreach(var customer in customerQuery2)

{

Console.WriteLine(customer.LastName + ",
" +
customer.FirstName);

}


LINQ 查询语法与方法语法 (C#)

通过使用 C# 3.0 中引入的声明性查询语法,介绍性 LINQ 文档中的多数查询都被编写为查询表达式。

但是,.NET 公共语言运行时 (CLR) 本身并不具有查询语法的概念。

因此,在编译时,查询表达式会转换为 CLR 确实了解的内容:方法调用。

这些方法称为“标准查询运算符”,它们具有如下名称:Where、Select、GroupBy、Join、Max、Average

等。可以通过使用方法语法而非查询语法来直接调用这些方法。

通常我们建议使用查询语法,因为它通常更简单、更易读;但是方法语法和查询语法之间并无语义上的区别。

此外,一些查询(如检索匹配指定条件的元素数的那些查询或检索具有源序列中的最大值的元素的查询)只能表示为方法调用。

System.Linq

命名空间中的标准查询运算符的参考文档通常使用方法语法。

因此,即使在开始编写 LINQ 查询时,熟悉如何在查询和查询表达式本身中使用方法语法也非常有用。

标准查询运算符扩展方法

下面的示例演示简单的查询表达式和编写为基于方法的查询的语义上等效的查询。

C#

class QueryVMethodSyntax

{

static

void Main()

{

int[] numbers = { 5, 10, 8, 3, 6, 12};

//Query syntax:

IEnumerable<int> numQuery1 =

from num in numbers

where num % 2 == 0

orderby num

select num;

//Method syntax:

IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);

foreach (int i in numQuery1)

{

Console.Write(i + " ");

}

Console.WriteLine(System.Environment.NewLine);

foreach (int i in numQuery2)

{

Console.Write(i + " ");

}

// Keep the console open in debug mode.

Console.WriteLine(System.Environment.NewLine);

Console.WriteLine("Press any key to exit");

Console.ReadKey();

}

}

/*

Output:

6 8 10 12

6 8 10 12

*/

复制代码

两个示例的输出是相同的。

您可以看到两种形式的查询变量的类型是相同的:IEnumerable(Of
T)。

若要了解基于方法的查询,让我们进一步地分析它。

注意,在表达式的右侧,where 子句现在表示为对 numbers 对象的实例方法,在您重新调用该对象时其类型为 IEnumerable<int>。

如果您熟悉泛型

IEnumerable(Of
T)

接口,那么您就会了解,它不具有 Where

方法。

但是,如果您在 Visual
Studio IDE 中调用 IntelliSense 完成列表,那么您不仅将看到 Where

方法,而且还会看到许多其他方法,如 Select、SelectMany、Join

和 Orderby。

下面是所有标准查询运算符。

尽管看起来

IEnumerable(Of
T)

似乎已被重新定义以包括这些附加方法,但事实上并非如此。

这些标准查询运算符作为一种新的方法(称为“扩展方法”)实现。

扩展方法可“扩展”现有类型;可如对类型的实例方法一样调用。

标准查询运算符可扩展

IEnumerable(Of
T),这就是您可以编写 numbers.Where(...) 的原因。

若要开始使用 LINQ,您实际需要了解的有关扩展方法的所有内容是,如何通过使用正确的 using 指令将它们置于您的应用程序中的范围内。

您可以在如何:创建
LINQ 项目中进一步了解相关信息。

从应用程序的角度来看,扩展方法和正常的实例方法是相同的。

Lambda 表达式

请注意,在上面的示例中,条件表达式 (num
% 2 == 0) 是作为内联参数传递到 Where

方法的:Where(num
=> num % 2 == 0). 。此内联表达式称为 lambda 表达式。

将代码编写为匿名方法或泛型委托或表达式树是一种便捷的方法,否则编写起来就要麻烦得多。

在 C# 中,=> 是 lambda 运算符,可读为“goes
to”。

运算符左侧的 num 是输入变量,与查询表达式中的 num 相对应。

编译器可推断 num 的类型,因为它了解 numbers 是泛型

IEnumerable(Of
T)

类型。

lambda 表达式与查询语法中的表达式或任何其他 C# 表达式或语句中的表达式相同;它可以包括方法调用和其他复杂逻辑。

“返回值”就是表达式结果。

若要开始使用 LINQ,您不必大量使用 lambda。

但是,特定查询只可使用方法语法表示,其中一些查询需要使用 lambda 表达式。

进一步熟悉 lambda 后,您会发现,在 LINQ 工具栏中,它们是强大且灵活的工具。

查询的组合性

在上面的代码示例中,请注意 OrderBy

方法是通过在对 Where

的调用中使用点运算符来调用的。

Where

生成筛选序列,然后 Orderby

通过对该序列排序来对它进行操作。

因为查询会返回 IEnumerable,所以您可通过将方法调用链接在一起,在方法语法中将这些查询组合起来。

这就是在您通过使用查询语法编写查询时编译器在后台所执行的操作。

并且由于查询变量不存储查询的结果,因此您可以随时修改它或将它用作新查询的基础,即使在执行它后。

输出序列可能不包含源序列的任何元素或元素属性。

输出可能是通过将源元素用作输入参数计算出的值的序列。

在执行下面这个简单查询时,此查询会输出一个字符串序列,该序列值表示根据 double 类型的元素的源序列进行的计算。
注意

如果查询将转换为某个其他域,则不支持在查询表达式中调用方法。

例如,不能在 LINQ
to SQL 中调用一般 C# 方法,因为 SQL Server 没有该方法的上下文。

但是,可以将存储过程映射到方法,然后调用方法。

C#

class FormatQuery

{

static

void Main()

{

//
Data source.

double[]
radii = { 1, 2, 3 };

//
Query.

IEnumerable<string>
query =

from rad in radii

select String.Format("Area
= {0}",
(rad * rad) * 3.14);

//
Query execution.

foreach (string s in query)

Console.WriteLine(s);

//
Keep the console open in debug mode.

Console.WriteLine("Press
any key to exit.");

Console.ReadKey();

}

}

/* Output:

Area = 3.14

Area = 12.56

Area = 28.26

*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: